Skip to content

Commit

Permalink
Merge pull request #199 from tgreenx/update-cds-cdnskey
Browse files Browse the repository at this point in the history
Extend CDS/CDNSKEY support
  • Loading branch information
tgreenx authored Sep 4, 2024
2 parents 0ea8d21 + 5f602e9 commit 8932dac
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 51 deletions.
8 changes: 5 additions & 3 deletions lib/Zonemaster/LDNS/RR/CDNSKEY.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package Zonemaster::LDNS::RR::CDNSKEY;
use strict;
use warnings;

use parent 'Zonemaster::LDNS::RR';
use parent 'Zonemaster::LDNS::RR::DNSKEY';

1;

Expand All @@ -13,10 +13,12 @@ Zonemaster::LDNS::RR::CDNSKEY - Type CDNSKEY record
=head1 DESCRIPTION
A subclass of L<Zonemaster::LDNS::RR>, so it has all the methods of that class available in addition to the ones documented here.
A subclass of L<Zonemaster::LDNS::RR::DNSKEY>, so it has all the methods of that class available in addition to the ones documented here.
=head1 METHODS
No RDATA methods implemented yet.
No other specific methods implemented.
Note that the inherited parent methods L<Zonemaster::LDNS::RR::DNSKEY/keytag()> and L<Zonemaster::LDNS::RR::DNSKEY/ds($hash)> will always return 0, as LDNS currently only supports the DNSKEY RR type for those methods.
=cut
8 changes: 5 additions & 3 deletions lib/Zonemaster/LDNS/RR/CDS.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package Zonemaster::LDNS::RR::CDS;
use strict;
use warnings;

use parent 'Zonemaster::LDNS::RR';
use parent 'Zonemaster::LDNS::RR::DS';

1;

Expand All @@ -13,10 +13,12 @@ Zonemaster::LDNS::RR::CDS - Type CDS record
=head1 DESCRIPTION
A subclass of L<Zonemaster::LDNS::RR>, so it has all the methods of that class available in addition to the ones documented here.
A subclass of L<Zonemaster::LDNS::RR::DS>, so it has all the methods of that class available in addition to the ones documented here.
=head1 METHODS
No RDATA methods implemented yet.
No other specific methods implemented.
Note that the inherited parent methods L<Zonemaster::LDNS::RR::DS/verify($other)> will always return false, as LDNS currently only supports the DS and DNSKEY RR types for this method.
=cut
8 changes: 8 additions & 0 deletions lib/Zonemaster/LDNS/RR/DNSKEY.pm
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ Returns the algorithm number.
Returns the cryptographic key in binary form.
=item hexkeydata()
Returns the cryptographic key as a hexadecimal string.
=item keytag()
Calculates the keytag.
=item ds($hash)
Returns a L<Zonemaster::LDNS::RR::DS> record matching this key. The argument must be one of the strings 'sha1', 'sha256', 'sha384' or 'gost'. GOST may not
Expand Down
8 changes: 4 additions & 4 deletions lib/Zonemaster/LDNS/RR/DS.pm
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ A subclass of L<Zonemaster::LDNS::RR>, so it has all the methods of that class a
Returns the keytag value.
=item digtype()
Returns the numeric digest type.
=item algorithm()
Returns the algorithm number.
=item digtype()
Returns the numeric digest type.
=item digest()
Returns the cryptographic digest in binary form.
Expand Down
10 changes: 10 additions & 0 deletions src/LDNS.xs
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,16 @@ rr_dnskey_keydata(obj)
OUTPUT:
RETVAL

char *
rr_dnskey_hexkeydata(obj)
Zonemaster::LDNS::RR::DNSKEY obj;
CODE:
RETVAL = D_STRING(obj,3);
OUTPUT:
RETVAL
CLEANUP:
free(RETVAL);

U16
rr_dnskey_keytag(obj)
Zonemaster::LDNS::RR::DNSKEY obj;
Expand Down
151 changes: 110 additions & 41 deletions t/rr.t
Original file line number Diff line number Diff line change
Expand Up @@ -94,36 +94,82 @@ subtest 'TXT' => sub {
};

subtest 'DNSKEY' => sub {
SKIP: {
skip 'no network', 1 unless $ENV{TEST_WITH_NETWORK};

my $se = Zonemaster::LDNS->new( '192.36.144.107' );
my $pk = $se->query( 'se', 'DNSKEY' );
plan skip_all => 'No response, cannot test' if not $pk;
subtest 'Good RR' => sub {
my @data = (
q{dnskey.test. 0 IN DNSKEY 257 3 8 BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=},
);
my @rrs = map { Zonemaster::LDNS::RR->new($_) } @data;

foreach my $rr ( $pk->answer ) {
foreach my $rr ( @rrs ) {
isa_ok( $rr, 'Zonemaster::LDNS::RR::DNSKEY' );
ok( $rr->flags == 256 or $rr->flags == 257 );
is( $rr->protocol, 3 );
# Alg 8 has replaced 5. Now (February 2022) only alg 8 is used.
ok( $rr->algorithm == 8 );
}
}

my $data = decode_base64( "BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=");
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );
my ( $rr, @extra ) = $p->answer_unfiltered;
eq_or_diff \@extra, [], "no extra RRs found";
if ( !defined $rr ) {
BAIL_OUT( "no RR found" );
}
is $rr->keydata, "", "we're able to extract the public key field even when it's empty";
is $rr->keysize, -1, "insufficient data to calculate key size is reported as -1";
is( $rrs[0]->flags(), q{257} );
is( $rrs[0]->protocol(), q{3} );
is( $rrs[0]->algorithm(), q{8} );
ok( $rrs[0]->keydata() );
is( $rrs[0]->hexkeydata(), q{BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=} );
is( $rrs[0]->keytag(), q{27018} );
is( $rrs[0]->ds('sha256'), Zonemaster::LDNS::RR->new(q{dnskey.test. 0 IN DS 27018 8 2 cd9b8881a72400a89b0f5ec4b096b07469aa3b316e870291d9e4e0f30f7dd4ed} ));
is( $rrs[0]->keysize(), q{344} );
};

subtest 'Empty RDATA' => sub {
my $data = decode_base64( "BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=");
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );
my ( $rr, @extra ) = $p->answer_unfiltered;
eq_or_diff \@extra, [], "no extra RRs found";
if ( !defined $rr ) {
BAIL_OUT( "no RR found" );
}
is $rr->keydata, "", "we're able to extract the public key field even when it's empty";
is $rr->hexkeydata, undef, "hexkeydata() returns undef when the public key field is empty";
is $rr->keysize, -1, "insufficient data to calculate key size is reported as -1";

my ( @rrs ) = $p->answer;
eq_or_diff \@rrs, [], "DNSKEY record with empty public key is filtered out by answer()";
};
};

subtest 'CDNSKEY' => sub {
subtest 'Good RR' => sub {
my @data = (
q{cdnskey.test. 0 IN CDNSKEY 257 3 8 BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=},
);
my @rrs = map { Zonemaster::LDNS::RR->new($_) } @data;

foreach my $rr ( @rrs ) {
isa_ok( $rr, 'Zonemaster::LDNS::RR::CDNSKEY' );
}

is( $rrs[0]->flags(), q{257} );
is( $rrs[0]->protocol(), q{3} );
is( $rrs[0]->algorithm(), q{8} );
ok( $rrs[0]->keydata() );
is( $rrs[0]->hexkeydata(), q{BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=} );
is( $rrs[0]->keytag(), q{0} ); # RR type not supported by LDNS
is( $rrs[0]->ds('sha256'), undef ); # RR type not supported by LDNS
is( $rrs[0]->keysize(), q{344} );
};

my ( @rrs ) = $p->answer;
eq_or_diff \@rrs, [], "DNSKEY record with empty public key is filtered out by answer()";
subtest 'Empty RDATA' => sub {
my $data = decode_base64( "BleFgAABAAEAAAAADW5sYWdyaWN1bHR1cmUCbmwAAAEAAcAMADAAAQAAAAAABAEBAwg=");
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );
my ( $rr, @extra ) = $p->answer_unfiltered;
eq_or_diff \@extra, [], "no extra RRs found";
if ( !defined $rr ) {
BAIL_OUT( "no RR found" );
}
is $rr->keydata, "", "we're able to extract the public key field even when it's empty";
is $rr->hexkeydata, undef, "hexkeydata() returns undef when the public key field is empty";
is $rr->keysize, -1, "insufficient data to calculate key size is reported as -1";

my ( @rrs ) = $p->answer;
eq_or_diff \@rrs, [], "CDNSKEY record with empty public key is filtered out by answer()";
};
};


subtest 'RRSIG' => sub {
SKIP: {
skip 'no network', 1 unless $ENV{TEST_WITH_NETWORK};
Expand Down Expand Up @@ -180,28 +226,51 @@ subtest 'From string' => sub {
};

subtest 'DS' => sub {
SKIP: {
skip 'no network', 1 unless $ENV{TEST_WITH_NETWORK};
subtest 'Good RR' => sub {
my @data = (
q{nic.se 3600 IN DS 22643 13 2 aa0b38f6755c2777992a74935d50a2a3480effef1a60bf8643d12c307465c9da},
);
my @rrs = map { Zonemaster::LDNS::RR->new($_) } @data;

foreach my $rr ( @rrs ) {
isa_ok( $rr, 'Zonemaster::LDNS::RR::DS' );
}

my $se = Zonemaster::LDNS->new( '192.36.144.107' );
my $pd = $se->query( 'nic.se', 'DS' );
plan skip_all => 'No response, cannot test' if not $pd;
my $key = Zonemaster::LDNS::RR->new( 'nic.se IN DNSKEY 257 3 13 lkpZSlU70pd1LHrXqZttOAYKmX046YqYQg1aQJsv1y0xKr+qJS+3Ue1tM5VCYPU3lKuzq93nz0Lm/AV9jeoumQ==' );
my $other = Zonemaster::LDNS::RR->new( 'nic.se IN NS a.ns.se' );

# As of February 2022, new KSK with keytag 22643 and algo 13 is used
my $nic_key = Zonemaster::LDNS::RR->new(
'nic.se IN DNSKEY 257 3 13 lkpZSlU70pd1LHrXqZttOAYKmX046YqYQg1aQJsv1y0xKr+qJS+3Ue1tM5VCYPU3lKuzq93nz0Lm/AV9jeoumQ=='
is( $rrs[0]->keytag(), q{22643} );
is( $rrs[0]->algorithm(), q{13} );
is( $rrs[0]->digtype(), q{2} );
ok( $rrs[0]->digest() );
is( $rrs[0]->hexdigest(), q{aa0b38f6755c2777992a74935d50a2a3480effef1a60bf8643d12c307465c9da} );
ok( $rrs[0]->verify( $key ) );
ok( !$rrs[0]->verify( $other ) );
};
};

subtest 'CDS' => sub {
subtest 'Good RR' => sub {
my @data = (
q{nic.se 3600 IN CDS 22643 13 2 aa0b38f6755c2777992a74935d50a2a3480effef1a60bf8643d12c307465c9da},
);
my $made = Zonemaster::LDNS::RR->new_from_string( 'nic.se IN NS a.ns.se' );
foreach my $rr ( $pd->answer ) {
isa_ok( $rr, 'Zonemaster::LDNS::RR::DS' );
is( $rr->keytag, 22643 );
is( $rr->algorithm, 13 );
ok( $rr->digtype == 1 or $rr->digtype == 2 );
ok( $rr->hexdigest eq 'aa0b38f6755c2777992a74935d50a2a3480effef1a60bf8643d12c307465c9da' );
ok( $rr->verify( $nic_key ), 'derived from expected DNSKEY' );
ok( !$rr->verify( $made ), 'does not match a non-DS non-DNSKEY record' );
my @rrs = map { Zonemaster::LDNS::RR->new($_) } @data;

foreach my $rr ( @rrs ) {
isa_ok( $rr, 'Zonemaster::LDNS::RR::CDS' );
}
}

my $key = Zonemaster::LDNS::RR->new( 'nic.se IN CDNSKEY 257 3 13 lkpZSlU70pd1LHrXqZttOAYKmX046YqYQg1aQJsv1y0xKr+qJS+3Ue1tM5VCYPU3lKuzq93nz0Lm/AV9jeoumQ==' );
my $other = Zonemaster::LDNS::RR->new( 'nic.se IN NS a.ns.se' );

is( $rrs[0]->keytag(), q{22643} );
is( $rrs[0]->algorithm(), q{13} );
is( $rrs[0]->digtype(), q{2} );
ok( $rrs[0]->digest() );
is( $rrs[0]->hexdigest(), q{aa0b38f6755c2777992a74935d50a2a3480effef1a60bf8643d12c307465c9da} );
ok( !$rrs[0]->verify( $key ) ); # RR type not supported by LDNS
ok( !$rrs[0]->verify( $other ) );
};
};

subtest 'NSEC3 without salt' => sub {
Expand Down

0 comments on commit 8932dac

Please sign in to comment.