File Coverage

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


line stmt bran cond sub pod time code
1             package Net::DNS::RR::DNSKEY;
2              
3 13     13   1432552 use strict;
  13         35  
  13         535  
4 13     13   69 use warnings;
  13         25  
  13         1154  
5             our $VERSION = (qw$Id: DNSKEY.pm 2042 2025-12-24 10:23:11Z willem $)[2];
6              
7 13     13   85 use base qw(Net::DNS::RR);
  13         50  
  13         1789  
8              
9              
10             =head1 NAME
11              
12             Net::DNS::RR::DNSKEY - DNS DNSKEY resource record
13              
14             =cut
15              
16 13     13   159 use integer;
  13         38  
  13         95  
17              
18 13     13   564 use Carp;
  13         22  
  13         1413  
19              
20 13     13   110 use constant BASE64 => defined eval { require MIME::Base64 };
  13         28  
  13         27  
  13         29647  
21              
22              
23             sub _decode_rdata { ## decode rdata from wire-format octet string
24 23     23   63 my ( $self, $data, $offset ) = @_;
25              
26 23         80 my $rdata = substr $$data, $offset, $self->{rdlength};
27 23         88 @{$self}{qw(flags protocol algorithm keybin)} = unpack 'n C2 a*', $rdata;
  23         87  
28 23         86 return;
29             }
30              
31              
32             sub _encode_rdata { ## encode rdata as wire-format octet string
33 25     25   43 my $self = shift;
34              
35 25         44 return pack 'n C2 a*', @{$self}{qw(flags protocol algorithm keybin)};
  25         158  
36             }
37              
38              
39             sub _format_rdata { ## format rdata portion of RR string.
40 10     10   20 my $self = shift;
41              
42 10         17 my @rdata = @{$self}{qw(flags protocol algorithm)};
  10         70  
43 10 100       27 if ( my $keybin = $self->keybin ) {
44 9         156 $self->_annotation( 'keytag', $self->keytag );
45 9         14 return $self->SUPER::_format_rdata() unless BASE64;
46 9         63 push @rdata, split /\s+/, MIME::Base64::encode($keybin);
47             } else {
48 1         3 push @rdata, '""';
49             }
50 10         112 return @rdata;
51             }
52              
53              
54             sub _parse_rdata { ## populate RR from rdata in argument list
55 21     21   89 my ( $self, @argument ) = @_;
56              
57 21         82 $self->flags( shift @argument );
58 21         93 $self->protocol( shift @argument );
59 21         43 my $algorithm = shift @argument;
60 21 100       145 $self->key(@argument) if $algorithm;
61 21         90 $self->algorithm($algorithm);
62 21         64 return;
63             }
64              
65              
66             sub _defaults { ## specify RR attribute default values
67 10     10   25 my $self = shift;
68              
69 10         48 $self->flags(256);
70 10         43 $self->protocol(3);
71 10         33 $self->algorithm(1);
72 10         60 $self->keybin('');
73 10         25 return;
74             }
75              
76              
77             sub flags {
78 47     47 1 1675 my ( $self, @value ) = @_;
79 47         109 for (@value) { $self->{flags} = 0 + $_ }
  41         175  
80 47   100     200 return $self->{flags} || 0;
81             }
82              
83              
84             sub zone {
85 30     30 1 6155 my ( $self, @value ) = @_;
86 30 100       100 if ( scalar @value ) {
87 7         24 for ( $self->{flags} |= 0x0100 ) {
88 7 100       34 $_ ^= 0x0100 unless shift @value;
89             }
90             }
91 30         190 return $self->{flags} & 0x0100;
92             }
93              
94              
95             sub revoke {
96 31     31 1 6628 my ( $self, @value ) = @_;
97 31 100       77 if ( scalar @value ) {
98 7         21 for ( $self->{flags} |= 0x0080 ) {
99 7 100       25 $_ ^= 0x0080 unless shift @value;
100             }
101             }
102 31         215 return $self->{flags} & 0x0080;
103             }
104              
105              
106             sub adt {
107 8     8 1 2950 my ( $self, @value ) = @_;
108 8 100       24 if ( scalar @value ) {
109 4         12 for ( $self->{flags} |= 0x0002 ) {
110 4 100       15 $_ ^= 0x0002 unless shift @value;
111             }
112             }
113 8         18 return $self->{flags} & 0x0002;
114             }
115              
116              
117             sub sep {
118 19     19 1 5390 my ( $self, @value ) = @_;
119 19 100       76 if ( scalar @value ) {
120 7         22 for ( $self->{flags} |= 0x0001 ) {
121 7 100       27 $_ ^= 0x0001 unless shift @value;
122             }
123             }
124 19         70 return $self->{flags} & 0x0001;
125             }
126              
127              
128             sub protocol {
129 58     58 1 3630 my ( $self, @value ) = @_;
130 58         115 for (@value) { $self->{protocol} = 0 + $_ }
  39         98  
131 58   100     320 return $self->{protocol} || 0;
132             }
133              
134              
135             sub algorithm {
136 97     97 1 3684 my ( $self, $arg ) = @_;
137              
138 97 100       264 unless ( ref($self) ) { ## class method or simple function
139 3         8 my $argn = pop;
140 3 100       20 return $argn =~ /\D/ ? _algbyname($argn) : _algbyval($argn);
141             }
142              
143 94 100       389 return $self->{algorithm} unless defined $arg;
144 51 100       211 return _algbyval( $self->{algorithm} ) if uc($arg) eq 'MNEMONIC';
145 46   100     123 return $self->{algorithm} = _algbyname($arg) || die _algbyname('') # disallow algorithm(0)
146             }
147              
148              
149             sub key {
150 39     39 1 107 my ( $self, @value ) = @_;
151 39 100       165 return MIME::Base64::encode( $self->keybin(), "" ) unless scalar @value;
152 23         200 return $self->keybin( MIME::Base64::decode( join "", @value ) );
153             }
154              
155              
156             sub keybin {
157 87     87 1 5054 my ( $self, @value ) = @_;
158 87         179 for (@value) { $self->{keybin} = $_ }
  38         95  
159 87   100     547 return $self->{keybin} || "";
160             }
161              
162              
163             sub publickey {
164 8     8 1 3275 my ( $self, @value ) = @_;
165 8         33 return $self->key(@value);
166             }
167              
168              
169             sub privatekeyname {
170 6     6 1 2492 my $self = shift;
171 6         21 my $name = $self->signame;
172 6         21 return sprintf 'K%s+%03d+%05d.private', $name, $self->algorithm, $self->keytag;
173             }
174              
175              
176             sub signame {
177 6     6 1 9 my $self = shift;
178 6         35 return lc $self->{owner}->fqdn;
179             }
180              
181              
182             sub keylength {
183 15     15 1 4313 my $self = shift;
184              
185 15   100     40 my $keybin = $self->keybin || return;
186              
187 13         52 local $_ = _algbyval( $self->{algorithm} );
188              
189 13 100       85 if (/^RSA/) {
    100          
190              
191             # Modulus length, see RFC 3110
192 9 100       36 if ( my $exp_length = unpack 'C', $keybin ) {
193              
194 7         37 return ( length($keybin) - $exp_length - 1 ) << 3;
195              
196             } else {
197 2         7 $exp_length = unpack 'x n', $keybin;
198 2         13 return ( length($keybin) - $exp_length - 3 ) << 3;
199             }
200              
201             } elsif (/^DSA/) {
202              
203             # Modulus length, see RFC 2536
204 2         7 my $T = unpack 'C', $keybin;
205 2         14 return ( $T << 6 ) + 512;
206             }
207              
208 2         16 return length($keybin) << 2; ## ECDSA / EdDSA
209             }
210              
211              
212             sub keytag {
213 49     49 1 4996 my $self = shift;
214              
215 49   100     174 my $keybin = $self->{keybin} || return;
216              
217             # RFC4034 Appendix B.1: most significant 16 bits of least significant 24 bits
218 46 100       136 return unpack 'n', substr $keybin, -3 if $self->{algorithm} == 1;
219              
220             # RFC4034 Appendix B
221 45         92 my $od = length($keybin) & 1;
222 45         231 my $rd = pack "n C2 a* x$od", @{$self}{qw(flags protocol algorithm)}, $keybin;
  45         268  
223 45         83 my $ac = 0;
224 45         670 $ac += $_ for unpack 'n*', $rd;
225 45         154 $ac += ( $ac >> 16 );
226 45         611 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   124 my $arg = shift;
268 48         94 my $key = uc $arg; # synthetic key
269 48         146 $key =~ s/[\W_]//g; # strip non-alphanumerics
270 48         123 my $val = $algbyname{$key};
271 48 100       305 return $val if defined $val;
272 10 100       485 return $key =~ /^\d/ ? $arg : croak qq[unknown algorithm $arg];
273             }
274              
275             sub _algbyval {
276 20     20   51 my $value = shift;
277 20   100     109 return $algbyval{$value} || return $value;
278             }
279             }
280              
281             ########################################
282              
283              
284             1;
285             __END__