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   21822 use strict;
  13         30  
  13         425  
4 13     13   69 use warnings;
  13         30  
  13         768  
5             our $VERSION = (qw$Id: DNSKEY.pm 1910 2023-03-30 19:16:30Z willem $)[2];
6              
7 13     13   93 use base qw(Net::DNS::RR);
  13         35  
  13         1328  
8              
9              
10             =head1 NAME
11              
12             Net::DNS::RR::DNSKEY - DNS DNSKEY resource record
13              
14             =cut
15              
16 13     13   191 use integer;
  13         38  
  13         94  
17              
18 13     13   557 use Carp;
  13         25  
  13         1313  
19              
20 13     13   94 use constant BASE64 => defined eval { require MIME::Base64 };
  13         33  
  13         32  
  13         20926  
21              
22              
23             sub _decode_rdata { ## decode rdata from wire-format octet string
24 37     37   101 my ( $self, $data, $offset ) = @_;
25              
26 37         164 my $rdata = substr $$data, $offset, $self->{rdlength};
27 37         296 @{$self}{qw(flags protocol algorithm keybin)} = unpack 'n C2 a*', $rdata;
  37         245  
28 37         172 return;
29             }
30              
31              
32             sub _encode_rdata { ## encode rdata as wire-format octet string
33 25     25   40 my $self = shift;
34              
35 25         44 return pack 'n C2 a*', @{$self}{qw(flags protocol algorithm keybin)};
  25         171  
36             }
37              
38              
39             sub _format_rdata { ## format rdata portion of RR string.
40 10     10   18 my $self = shift;
41              
42 10         19 my @rdata = @{$self}{qw(flags protocol algorithm)};
  10         36  
43 10 100       24 if ( my $keybin = $self->keybin ) {
44 9         52 $self->_annotation( 'Key ID =', $self->keytag );
45 9         13 return $self->SUPER::_format_rdata() unless BASE64;
46 9         109 push @rdata, split /\s+/, MIME::Base64::encode($keybin);
47             } else {
48 1         2 push @rdata, '""';
49             }
50 10         54 return @rdata;
51             }
52              
53              
54             sub _parse_rdata { ## populate RR from rdata in argument list
55 22     22   89 my ( $self, @argument ) = @_;
56              
57 22         72 $self->flags( shift @argument );
58 22         69 $self->protocol( shift @argument );
59 22         40 my $algorithm = shift @argument;
60 22 100       175 $self->key(@argument) if $algorithm;
61 22         66 $self->algorithm($algorithm);
62 22         59 return;
63             }
64              
65              
66             sub _defaults { ## specify RR attribute default values
67 10     10   644 my $self = shift;
68              
69 10         37 $self->flags(256);
70 10         32 $self->protocol(3);
71 10         29 $self->algorithm(1);
72 10         37 $self->keybin('');
73 10         22 return;
74             }
75              
76              
77             sub flags {
78 48     48 1 1748 my ( $self, @value ) = @_;
79 48         99 for (@value) { $self->{flags} = 0 + $_ }
  42         194  
80 48   100     194 return $self->{flags} || 0;
81             }
82              
83              
84             sub zone {
85 30     30 1 4152 my ( $self, @value ) = @_;
86 30         77 for ( $self->{flags} |= 0 ) {
87 30 100       89 if ( scalar @value ) {
88 7         12 $_ |= 0x0100;
89 7 100       21 $_ ^= 0x0100 unless shift @value;
90             }
91             }
92 30         174 return $self->{flags} & 0x0100;
93             }
94              
95              
96             sub revoke {
97 31     31 1 4425 my ( $self, @value ) = @_;
98 31         64 for ( $self->{flags} |= 0 ) {
99 31 100       86 if ( scalar @value ) {
100 7         11 $_ |= 0x0080;
101 7 100       20 $_ ^= 0x0080 unless shift @value;
102             }
103             }
104 31         186 return $self->{flags} & 0x0080;
105             }
106              
107              
108             sub sep {
109 19     19 1 3934 my ( $self, @value ) = @_;
110 19         40 for ( $self->{flags} |= 0 ) {
111 19 100       54 if ( scalar @value ) {
112 7         11 $_ |= 0x0001;
113 7 100       22 $_ ^= 0x0001 unless shift @value;
114             }
115             }
116 19         57 return $self->{flags} & 0x0001;
117             }
118              
119              
120             sub protocol {
121 59     59 1 2781 my ( $self, @value ) = @_;
122 59         113 for (@value) { $self->{protocol} = 0 + $_ }
  40         84  
123 59   100     303 return $self->{protocol} || 0;
124             }
125              
126              
127             sub algorithm {
128 98     98 1 2786 my ( $self, $arg ) = @_;
129              
130 98 100       249 unless ( ref($self) ) { ## class method or simple function
131 3         5 my $argn = pop;
132 3 100       18 return $argn =~ /\D/ ? _algbyname($argn) : _algbyval($argn);
133             }
134              
135 95 100       329 return $self->{algorithm} unless defined $arg;
136 52 100       156 return _algbyval( $self->{algorithm} ) if uc($arg) eq 'MNEMONIC';
137 47   100     103 return $self->{algorithm} = _algbyname($arg) || die _algbyname('') # disallow algorithm(0)
138             }
139              
140              
141             sub key {
142 40     40 1 92 my ( $self, @value ) = @_;
143 40 100       140 return MIME::Base64::encode( $self->keybin(), "" ) unless scalar @value;
144 24         265 return $self->keybin( MIME::Base64::decode( join "", @value ) );
145             }
146              
147              
148             sub keybin {
149 89     89 1 3646 my ( $self, @value ) = @_;
150 89         185 for (@value) { $self->{keybin} = $_ }
  39         84  
151 89   100     426 return $self->{keybin} || "";
152             }
153              
154              
155             sub publickey {
156 8     8 1 2378 my ( $self, @value ) = @_;
157 8         27 return $self->key(@value);
158             }
159              
160              
161             sub privatekeyname {
162 6     6 1 1691 my $self = shift;
163 6         27 my $name = $self->signame;
164 6         17 return sprintf 'K%s+%03d+%05d.private', $name, $self->algorithm, $self->keytag;
165             }
166              
167              
168             sub signame {
169 6     6 1 13 my $self = shift;
170 6         27 return lc $self->{owner}->fqdn;
171             }
172              
173              
174             sub keylength {
175 16     16 1 3931 my $self = shift;
176              
177 16   100     38 my $keybin = $self->keybin || return;
178              
179 14         34 local $_ = _algbyval( $self->{algorithm} );
180              
181 14 100       72 if (/^RSA/) {
    100          
182              
183             # Modulus length, see RFC 3110
184 9 100       35 if ( my $exp_length = unpack 'C', $keybin ) {
185              
186 7         48 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         6 my $T = unpack 'C', $keybin;
197 2         26 return ( $T << 6 ) + 512;
198             }
199              
200 3         15 return length($keybin) << 2; ## ECDSA / EdDSA
201             }
202              
203              
204             sub keytag {
205 49     49 1 3680 my $self = shift;
206              
207 49   100     132 my $keybin = $self->{keybin} || return;
208              
209             # RFC4034 Appendix B.1: most significant 16 bits of least significant 24 bits
210 46 100       114 return unpack 'n', substr $keybin, -3 if $self->{algorithm} == 1;
211              
212             # RFC4034 Appendix B
213 45         83 my $od = length($keybin) & 1;
214 45         99 my $rd = pack "n C2 a* x$od", @{$self}{qw(flags protocol algorithm)}, $keybin;
  45         242  
215 45         84 my $ac = 0;
216 45         619 $ac += $_ for unpack 'n*', $rd;
217 45         149 $ac += ( $ac >> 16 );
218 45         499 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   85 my $arg = shift;
258 49         87 my $key = uc $arg; # synthetic key
259 49         136 $key =~ s/[\W_]//g; # strip non-alphanumerics
260 49         98 my $val = $algbyname{$key};
261 49 100       218 return $val if defined $val;
262 10 100       366 return $key =~ /^\d/ ? $arg : croak qq[unknown algorithm $arg];
263             }
264              
265             sub _algbyval {
266 21     21   35 my $value = shift;
267 21   100     93 return $algbyval{$value} || return $value;
268             }
269             }
270              
271             ########################################
272              
273              
274             1;
275             __END__