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