# $Id: 72-TSIG-verify.t 1748 2019-07-15 07:57:00Z willem $	-*-perl-*-

use strict;
use Test::More;
use Net::DNS;

my %prerequisite = (
	'Digest::HMAC' => 1.03,
	'Digest::MD5'  => 2.13,
	'Digest::SHA'  => 5.23,
	'MIME::Base64' => 2.13,
	);

foreach my $package ( sort keys %prerequisite ) {
	my @revision = grep $_, $prerequisite{$package};
	next if eval "use $package @revision; 1;";
	plan skip_all => "missing prerequisite $package @revision";
	exit;
}

plan tests => 28;


my $tsig = new Net::DNS::RR( type => 'TSIG' );
my $class = ref($tsig);


my $privatekey = 'Khmac-sha1.example.+161+39562.private';
END { unlink($privatekey) if defined $privatekey; }

open( KEY, ">$privatekey" ) or die "$privatekey $!";
print KEY <<'END';
Private-key-format: v1.2
Algorithm: 161 (HMAC_SHA1)
Key: xdX9m8UtQNbJUzUgQ4xDtUNZAmU=
END
close KEY;


my $publickey = 'Khmac-md5.example.+157+53335.key';
END { unlink($publickey) if defined $publickey; }

open( KEY, ">$publickey" ) or die "$publickey $!";
print KEY <<'END';
HMAC-MD5.example. IN KEY 512 3 157 ARDJZgtuTDzAWeSGYPAu9uJUkX0=
END
close KEY;


{
	my $packet = new Net::DNS::Packet('query.example');
	$packet->sign_tsig($privatekey);
	$packet->data;

	my $verified = $packet->verify();
	ok( $verified, 'verify signed packet' );
	is( ref($verified),	$class,	   'packet->verify returns TSIG' );
	is( $packet->verifyerr, 'NOERROR', 'observe packet->verifyerr' );
}


{
	my $packet = new Net::DNS::Packet('query.example');
	$packet->sign_tsig($privatekey);
	$packet->data;
	$packet->push( update => rr_add( type => 'NULL' ) );

	my $verified = $packet->verify();
	ok( !$verified, 'unverifiable signed packet' );
	is( $verified,		undef,	  'failed packet->verify returns undef' );
	is( $packet->verifyerr, 'BADSIG', 'observe packet->verifyerr' );
}


{
	my $query = new Net::DNS::Packet('query.example');
	$query->sign_tsig($privatekey);
	$query->data;

	my $reply = $query->reply;
	$reply->sign_tsig($query);
	$reply->data;

	my $verified = $reply->verify($query);
	ok( $verified, 'verify reply packet' );
	is( $reply->verifyerr, 'NOERROR', 'observe packet->verifyerr' );
}


{
	my @packet = map { new Net::DNS::Packet($_) } 0 .. 3;
	my $signed = $privatekey;
	foreach my $packet (@packet) {
		$signed = $packet->sign_tsig($signed);
		$packet->data;
		is( ref($signed), $class, 'sign multi-packet' );
	}

	my @verified;
	foreach my $packet (@packet) {
		my ($verified) = $packet->verify(@verified);
		@verified = ($verified);
		ok( $verified, 'verify multi-packet' );
	}

	my @state;
	$packet[2]->sigrr->fudge(0);
	foreach my $packet (@packet) {
		my $tsig = $packet->verify(@state);
		@state = ($tsig);
		my $result = $packet->verifyerr;
		ok( $result, "unverifiable multi-packet: $result" );
	}
}


{
	my $packet = new Net::DNS::Packet('query.example');
	$packet->sign_tsig( $privatekey, fudge => 0 );
	my $encoded = $packet->data;
	sleep 2;						# guarantee one complete second delay

	my $query = new Net::DNS::Packet( \$encoded );
	$query->verify();
	is( $query->verifyerr, 'BADTIME', 'unverifiable query packet: BADTIME' );
}


{
	my $packet = new Net::DNS::Packet();
	$packet->sign_tsig($privatekey);
	$packet->sigrr->error('BADTIME');
	my $encoded = $packet->data;
	my $decoded = new Net::DNS::Packet( \$encoded );
	ok( $decoded->sigrr->other, 'time appended to BADTIME response' );
}


{
	my $query = new Net::DNS::Packet('query.example');
	$query->sign_tsig($privatekey);
	$query->data;

	my $reply = $query->reply;
	$reply->sign_tsig($publickey);
	$reply->data;

	my $verified = $reply->verify($query);
	is( $reply->verifyerr, 'BADKEY', 'unverifiable reply packet: BADKEY' );
}


{
	my $packet0 = new Net::DNS::Packet();
	my $chain   = $packet0->sign_tsig($privatekey);
	$packet0->data;
	my $packet1 = new Net::DNS::Packet();
	$packet1->sign_tsig($chain);
	$packet1->data;

	my $packetx = new Net::DNS::Packet();
	$packetx->sign_tsig($publickey);
	$packetx->data;
	my $tsig     = $packetx->verify();
	my $verified = $packet1->verify($tsig);
	is( $packet1->verifyerr, 'BADKEY', 'unverifiable multi-packet: BADKEY' );
}


{
	my $packet = new Net::DNS::Packet();
	$packet->sign_tsig($publickey);
	$packet->data;
	$packet->sigrr->macbin( substr $packet->sigrr->macbin, 0, 9 );

	$packet->verify();
	is( $packet->verifyerr, 'BADTRUNC', 'signature too short: BADTRUNC' );
}


{
	my $packet = new Net::DNS::Packet();
	$packet->sign_tsig($publickey);
	$packet->data;
	my $macbin = $packet->sigrr->macbin;
	$packet->sigrr->macbin( join '', $packet->sigrr->macbin, 'x' );

	$packet->verify();
	is( $packet->verifyerr, 'BADTRUNC', 'signature too long: BADTRUNC' );
}


{
	my $packet = new Net::DNS::Packet();
	$packet->sign_tsig($privatekey);

	my $null = new Net::DNS::RR( type => 'NULL' );
	eval { $packet->sigrr->verify($null); };
	my ($exception) = split /\n/, "$@\n";
	ok( $exception, "unexpected argument\t[$exception]" );
}


{
	my $packet = new Net::DNS::Packet();
	$packet->sign_tsig($privatekey);

	my $null = new Net::DNS::RR( type => 'NULL' );
	eval { $packet->sigrr->verify( $packet, $null ); };
	my ($exception) = split /\n/, "$@\n";
	ok( $exception, "unexpected argument\t[$exception]" );
}


__END__

