@@ -17,7 +17,7 @@ use thiserror::Error;
1717
1818use crate :: dnssec:: crypto:: Digest ;
1919use crate :: error:: ProtoError ;
20- use crate :: rr:: { Name , RData , Record , rdata:: tsig:: TsigAlgorithm } ;
20+ use crate :: rr:: { Name , RData , Record , RecordType , rdata:: tsig:: TsigAlgorithm } ;
2121use crate :: serialize:: binary:: { BinEncodable , BinEncoder , DecodeError , NameEncoding } ;
2222
2323mod algorithm;
@@ -384,7 +384,7 @@ impl Clone for DnsSecError {
384384}
385385
386386/// DNSSEC status of an answer
387- #[ derive( Clone , Copy , Debug ) ]
387+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
388388pub enum DnssecSummary {
389389 /// All records have been DNSSEC validated
390390 Secure ,
@@ -396,9 +396,15 @@ pub enum DnssecSummary {
396396
397397impl DnssecSummary {
398398 /// Whether the records have been DNSSEC validated or not
399+ ///
400+ /// RRSIGs are skipped, since only the RRSIG used for verification carries the RRset's proof.
399401 pub fn from_records < ' a > ( records : impl Iterator < Item = & ' a Record > ) -> Self {
400402 let mut all_secure = None ;
401403 for record in records {
404+ if record. record_type ( ) == RecordType :: RRSIG {
405+ continue ;
406+ }
407+
402408 match & record. proof {
403409 Proof :: Secure => {
404410 all_secure. get_or_insert ( true ) ;
@@ -514,3 +520,117 @@ mod test_utils {
514520 ) ;
515521 }
516522}
523+
524+ #[ cfg( test) ]
525+ mod tests {
526+ use alloc:: vec;
527+ use core:: net:: Ipv4Addr ;
528+
529+ use super :: rdata:: { DNSSECRData , RRSIG , SigInput } ;
530+ use super :: * ;
531+ use crate :: rr:: SerialNumber ;
532+
533+ fn a_record ( name : & Name , proof : Proof ) -> Record {
534+ let mut record = Record :: from_rdata (
535+ name. clone ( ) ,
536+ 3600 ,
537+ RData :: A ( Ipv4Addr :: new ( 192 , 0 , 2 , 1 ) . into ( ) ) ,
538+ ) ;
539+ record. proof = proof;
540+ record
541+ }
542+
543+ fn rrsig_record ( name : & Name , algorithm : Algorithm , proof : Proof ) -> Record {
544+ let input = SigInput {
545+ type_covered : RecordType :: A ,
546+ algorithm,
547+ num_labels : 2 ,
548+ original_ttl : 3600 ,
549+ sig_expiration : SerialNumber ( 0 ) ,
550+ sig_inception : SerialNumber ( 0 ) ,
551+ key_tag : 0 ,
552+ signer_name : Name :: root ( ) ,
553+ } ;
554+ let mut record = Record :: from_rdata (
555+ name. clone ( ) ,
556+ 3600 ,
557+ RData :: DNSSEC ( DNSSECRData :: RRSIG ( RRSIG :: from_sig ( input, vec ! [ ] ) ) ) ,
558+ ) ;
559+ record. proof = proof;
560+ record
561+ }
562+
563+ /// Only the RRSIG used for verification is marked, the other one stays Indeterminate.
564+ #[ test]
565+ fn summary_ignores_unused_rrsig ( ) {
566+ let name = Name :: from_ascii ( "www.example." ) . unwrap ( ) ;
567+ let records = [
568+ a_record ( & name, Proof :: Secure ) ,
569+ rrsig_record ( & name, Algorithm :: ECDSAP256SHA256 , Proof :: Secure ) ,
570+ rrsig_record ( & name, Algorithm :: RSASHA256 , Proof :: Indeterminate ) ,
571+ ] ;
572+
573+ assert_eq ! (
574+ DnssecSummary :: from_records( records. iter( ) ) ,
575+ DnssecSummary :: Secure
576+ ) ;
577+ }
578+
579+ #[ test]
580+ fn summary_follows_rrset_proof ( ) {
581+ let name = Name :: from_ascii ( "www.example." ) . unwrap ( ) ;
582+ for ( proof, expected) in [
583+ ( Proof :: Secure , DnssecSummary :: Secure ) ,
584+ ( Proof :: Insecure , DnssecSummary :: Insecure ) ,
585+ ( Proof :: Indeterminate , DnssecSummary :: Insecure ) ,
586+ ( Proof :: Bogus , DnssecSummary :: Bogus ) ,
587+ ] {
588+ let records = [
589+ a_record ( & name, proof) ,
590+ rrsig_record ( & name, Algorithm :: ECDSAP256SHA256 , proof) ,
591+ ] ;
592+ assert_eq ! ( DnssecSummary :: from_records( records. iter( ) ) , expected) ;
593+ }
594+ }
595+
596+ /// The proof of an RRSIG must not override the proof of the records it covers.
597+ #[ test]
598+ fn summary_ignores_rrsig_proof ( ) {
599+ let name = Name :: from_ascii ( "www.example." ) . unwrap ( ) ;
600+ let records = [
601+ a_record ( & name, Proof :: Bogus ) ,
602+ rrsig_record ( & name, Algorithm :: ECDSAP256SHA256 , Proof :: Secure ) ,
603+ ] ;
604+ assert_eq ! (
605+ DnssecSummary :: from_records( records. iter( ) ) ,
606+ DnssecSummary :: Bogus
607+ ) ;
608+
609+ let records = [
610+ a_record ( & name, Proof :: Insecure ) ,
611+ rrsig_record ( & name, Algorithm :: ECDSAP256SHA256 , Proof :: Secure ) ,
612+ ] ;
613+ assert_eq ! (
614+ DnssecSummary :: from_records( records. iter( ) ) ,
615+ DnssecSummary :: Insecure
616+ ) ;
617+ }
618+
619+ #[ test]
620+ fn summary_of_rrsigs_only_is_insecure ( ) {
621+ let name = Name :: from_ascii ( "www.example." ) . unwrap ( ) ;
622+ let records = [ rrsig_record (
623+ & name,
624+ Algorithm :: ECDSAP256SHA256 ,
625+ Proof :: Secure ,
626+ ) ] ;
627+ assert_eq ! (
628+ DnssecSummary :: from_records( records. iter( ) ) ,
629+ DnssecSummary :: Insecure
630+ ) ;
631+ assert_eq ! (
632+ DnssecSummary :: from_records( [ ] . iter( ) ) ,
633+ DnssecSummary :: Insecure
634+ ) ;
635+ }
636+ }
0 commit comments