File Coverage

blib/lib/Net/DNS/RR/DNSKEY.pm
Criterion Covered Total %
statement 127 127 100.0
branch 42 42 100.0
path n/a
condition 14 14 100.0
subroutine 27 27 100.0
pod 14 14 100.0
total 224 224 100.0


line stmt bran path cond sub pod time code
1               package Net::DNS::RR::DNSKEY;
2                
3 13       13   1008231 use strict;
  13           27  
  13           429  
4 13       13   50 use warnings;
  13           20  
  13           907  
5               our $VERSION = (qw$Id: DNSKEY.pm 2042 2025-12-24 10:23:11Z willem $)[2];
6                
7 13       13   84 use base qw(Net::DNS::RR);
  13           23  
  13           1390  
8                
9                
10               =head1 NAME
11                
12               Net::DNS::RR::DNSKEY - DNS DNSKEY resource record
13                
14               =cut
15                
16 13       13   78 use integer;
  13           41  
  13           78  
17                
18 13       13   435 use Carp;
  13           19  
  13           1112  
19                
20 13       13   59 use constant BASE64 => defined eval { require MIME::Base64 };
  13           49  
  13           20  
  13           18209  
21                
22                
23               sub _decode_rdata { ## decode rdata from wire-format octet string
24 13       13   35 my ( $self, $data, $offset ) = @_;
25                
26 13           35 my $rdata = substr $$data, $offset, $self->{rdlength};
27 13           38 @{$self}{qw(flags protocol algorithm keybin)} = unpack 'n C2 a*', $rdata;
  13           41  
28 13           25 return;
29               }
30                
31                
32               sub _encode_rdata { ## encode rdata as wire-format octet string
33 25       25   28 my $self = shift;
34                
35 25           35 return pack 'n C2 a*', @{$self}{qw(flags protocol algorithm keybin)};
  25           129  
36               }
37                
38                
39               sub _format_rdata { ## format rdata portion of RR string.
40 10       10   71 my $self = shift;
41                
42 10           13 my @rdata = @{$self}{qw(flags protocol algorithm)};
  10           25  
43 10 100         19 if ( my $keybin = $self->keybin ) {
44 9           1909 $self->_annotation( 'keytag', $self->keytag );
45 9           10 return $self->SUPER::_format_rdata() unless BASE64;
46 9           67 push @rdata, split /\s+/, MIME::Base64::encode($keybin);
47               } else {
48 1           2 push @rdata, '""';
49               }
50 10           38 return @rdata;
51               }
52                
53                
54               sub _parse_rdata { ## populate RR from rdata in argument list
55 21       21   60 my ( $self, @argument ) = @_;
56                
57 21           51 $self->flags( shift @argument );
58 21           52 $self->protocol( shift @argument );
59 21           35 my $algorithm = shift @argument;
60 21 100         100 $self->key(@argument) if $algorithm;
61 21           58 $self->algorithm($algorithm);
62 21           52 return;
63               }
64                
65                
66               sub _defaults { ## specify RR attribute default values
67 10       10   16 my $self = shift;
68                
69 10           30 $self->flags(256);
70 10           36 $self->protocol(3);
71 10           27 $self->algorithm(1);
72 10           37 $self->keybin('');
73 10           17 return;
74               }
75                
76                
77               sub flags {
78 47       47 1 1360 my ( $self, @value ) = @_;
79 47           81 for (@value) { $self->{flags} = 0 + $_ }
  41           132  
80 47     100     138 return $self->{flags} || 0;
81               }
82                
83                
84               sub zone {
85 30       30 1 4051 my ( $self, @value ) = @_;
86 30 100         56 if ( scalar @value ) {
87 7           27 for ( $self->{flags} |= 0x0100 ) {
88 7 100         16 $_ ^= 0x0100 unless shift @value;
89               }
90               }
91 30           187 return $self->{flags} & 0x0100;
92               }
93                
94                
95               sub revoke {
96 31       31 1 5765 my ( $self, @value ) = @_;
97 31 100         59 if ( scalar @value ) {
98 7           14 for ( $self->{flags} |= 0x0080 ) {
99 7 100         17 $_ ^= 0x0080 unless shift @value;
100               }
101               }
102 31           185 return $self->{flags} & 0x0080;
103               }
104                
105                
106               sub adt {
107 8       8 1 2148 my ( $self, @value ) = @_;
108 8 100         15 if ( scalar @value ) {
109 4           7 for ( $self->{flags} |= 0x0002 ) {
110 4 100         16 $_ ^= 0x0002 unless shift @value;
111               }
112               }
113 8           13 return $self->{flags} & 0x0002;
114               }
115                
116                
117               sub sep {
118 19       19 1 4950 my ( $self, @value ) = @_;
119 19 100         37 if ( scalar @value ) {
120 7           13 for ( $self->{flags} |= 0x0001 ) {
121 7 100         29 $_ ^= 0x0001 unless shift @value;
122               }
123               }
124 19           43 return $self->{flags} & 0x0001;
125               }
126                
127                
128               sub protocol {
129 58       58 1 3122 my ( $self, @value ) = @_;
130 58           88 for (@value) { $self->{protocol} = 0 + $_ }
  39           67  
131 58     100     289 return $self->{protocol} || 0;
132               }
133                
134                
135               sub algorithm {
136 97       97 1 2724 my ( $self, $arg ) = @_;
137                
138 97 100         218 unless ( ref($self) ) { ## class method or simple function
139 3           4 my $argn = pop;
140 3 100         13 return $argn =~ /\D/ ? _algbyname($argn) : _algbyval($argn);
141               }
142                
143 94 100         256 return $self->{algorithm} unless defined $arg;
144 51 100         132 return _algbyval( $self->{algorithm} ) if uc($arg) eq 'MNEMONIC';
145 46     100     122 return $self->{algorithm} = _algbyname($arg) || die _algbyname('') # disallow algorithm(0)
146               }
147                
148                
149               sub key {
150 39       39 1 78 my ( $self, @value ) = @_;
151 39 100         134 return MIME::Base64::encode( $self->keybin(), "" ) unless scalar @value;
152 23           163 return $self->keybin( MIME::Base64::decode( join "", @value ) );
153               }
154                
155                
156               sub keybin {
157 87       87 1 4294 my ( $self, @value ) = @_;
158 87           136 for (@value) { $self->{keybin} = $_ }
  38           70  
159 87     100     366 return $self->{keybin} || "";
160               }
161                
162                
163               sub publickey {
164 8       8 1 2622 my ( $self, @value ) = @_;
165 8           21 return $self->key(@value);
166               }
167                
168                
169               sub privatekeyname {
170 6       6 1 1673 my $self = shift;
171 6           15 my $name = $self->signame;
172 6           15 return sprintf 'K%s+%03d+%05d.private', $name, $self->algorithm, $self->keytag;
173               }
174                
175                
176               sub signame {
177 6       6 1 7 my $self = shift;
178 6           28 return lc $self->{owner}->fqdn;
179               }
180                
181                
182               sub keylength {
183 15       15 1 3836 my $self = shift;
184                
185 15     100     28 my $keybin = $self->keybin || return;
186                
187 13           26 local $_ = _algbyval( $self->{algorithm} );
188                
189 13 100         54 if (/^RSA/) {
    100            
190                
191               # Modulus length, see RFC 3110
192 9 100         26 if ( my $exp_length = unpack 'C', $keybin ) {
193                
194 7           26 return ( length($keybin) - $exp_length - 1 ) << 3;
195                
196               } else {
197 2           5 $exp_length = unpack 'x n', $keybin;
198 2           10 return ( length($keybin) - $exp_length - 3 ) << 3;
199               }
200                
201               } elsif (/^DSA/) {
202                
203               # Modulus length, see RFC 2536
204 2           6 my $T = unpack 'C', $keybin;
205 2           10 return ( $T << 6 ) + 512;
206               }
207                
208 2           11 return length($keybin) << 2; ## ECDSA / EdDSA
209               }
210                
211                
212               sub keytag {
213 49       49 1 3778 my $self = shift;
214                
215 49     100     125 my $keybin = $self->{keybin} || return;
216                
217               # RFC4034 Appendix B.1: most significant 16 bits of least significant 24 bits
218 46 100         85 return unpack 'n', substr $keybin, -3 if $self->{algorithm} == 1;
219                
220               # RFC4034 Appendix B
221 45           69 my $od = length($keybin) & 1;
222 45           111 my $rd = pack "n C2 a* x$od", @{$self}{qw(flags protocol algorithm)}, $keybin;
  45           264  
223 45           58 my $ac = 0;
224 45           457 $ac += $_ for unpack 'n*', $rd;
225 45           154 $ac += ( $ac >> 16 );
226 45           488 return $ac & 0xFFFF;
227               }
228                
229                
230               ########################################
231                
232               {
233               my @algbyname = (
234               'DELETE' => 0, # [RFC4034][RFC4398][RFC8078]
235               'RSAMD5' => 1, # [RFC3110][RFC4034]
236               'DH' => 2, # [RFC2539]
237               'DSA' => 3, # [RFC3755][RFC2536]
238               ## Reserved => 4, # [RFC6725]
239               'RSASHA1' => 5, # [RFC3110][RFC4034]
240               'DSA-NSEC3-SHA1' => 6, # [RFC5155]
241               'RSASHA1-NSEC3-SHA1' => 7, # [RFC5155]
242               'RSASHA256' => 8, # [RFC5702]
243               ## Reserved => 9, # [RFC6725]
244               'RSASHA512' => 10, # [RFC5702]
245               ## Reserved => 11, # [RFC6725]
246               'ECC-GOST' => 12, # [RFC5933]
247               'ECDSAP256SHA256' => 13, # [RFC6605]
248               'ECDSAP384SHA384' => 14, # [RFC6605]
249               'ED25519' => 15, # [RFC8080]
250               'ED448' => 16, # [RFC8080]
251               'SM2SM3' => 17, # [RFC9563]
252               'ECC-GOST12' => 23, # [RFC9558]
253                
254               'INDIRECT' => 252, # [RFC4034]
255               'PRIVATEDNS' => 253, # [RFC4034]
256               'PRIVATEOID' => 254, # [RFC4034]
257               ## Reserved => 255, # [RFC4034]
258               );
259                
260               my %algbyval = reverse @algbyname;
261                
262               foreach (@algbyname) { s/[\W_]//g; } # strip non-alphanumerics
263               my @algrehash = map { /^\d/ ? ($_) x 3 : uc($_) } @algbyname;
264               my %algbyname = @algrehash; # work around broken cperl
265                
266               sub _algbyname {
267 48       48   72 my $arg = shift;
268 48           75 my $key = uc $arg; # synthetic key
269 48           112 $key =~ s/[\W_]//g; # strip non-alphanumerics
270 48           89 my $val = $algbyname{$key};
271 48 100         241 return $val if defined $val;
272 10 100         335 return $key =~ /^\d/ ? $arg : croak qq[unknown algorithm $arg];
273               }
274                
275               sub _algbyval {
276 20       20   30 my $value = shift;
277 20     100     56 return $algbyval{$value} || return $value;
278               }
279               }
280                
281               ########################################
282                
283                
284               1;
285               __END__