File Coverage

blib/lib/Net/DNS/RR/DNSKEY.pm
Criterion Covered Total %
statement 125 125 100.0
branch 38 38 100.0
condition 14 14 100.0
subroutine 26 26 100.0
pod 13 13 100.0
total 216 216 100.0


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