Avoid overriding $RUSTFLAGS when needed for rustc 1.63
[dnssec-prover] / src / validation.rs
index 692bb63fa7236d4b8e4919c6eb9bc8df58beebb9..fc188a46dff0d04c8ee3e330b12e63bcccc69f90 100644 (file)
@@ -9,6 +9,7 @@ use crate::base32;
 use crate::crypto;
 use crate::rr::*;
 use crate::ser::write_name;
+use crate::MAX_PROOF_STEPS;
 
 /// Gets the trusted root anchors
 ///
@@ -43,9 +44,12 @@ pub enum ValidationError {
        UnsupportedAlgorithm,
        /// The provided data was invalid or signatures did not validate.
        Invalid,
+       /// We would need to validate more than [`MAX_PROOF_STEPS`] sets of [`RRSig`]s to validate the
+       /// proof we were given.
+       ValidationCountLimited,
 }
 
-fn verify_rrsig<'a, RR: Record, Keys>(sig: &RRSig, dnskeys: Keys, mut records: Vec<&RR>)
+fn verify_rrsig<'a, RR: WriteableRecord, Keys>(sig: &RRSig, dnskeys: Keys, mut records: Vec<&RR>)
 -> Result<(), ValidationError>
 where Keys: IntoIterator<Item = &'a DnsKey> {
        for record in records.iter() {
@@ -68,15 +72,14 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
                                _ => return Err(ValidationError::UnsupportedAlgorithm),
                        };
 
-                       let mut signed_data = Vec::with_capacity(2048);
-                       signed_data.extend_from_slice(&sig.ty.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.alg.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.labels.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.orig_ttl.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.expiration.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.inception.to_be_bytes());
-                       signed_data.extend_from_slice(&sig.key_tag.to_be_bytes());
-                       write_name(&mut signed_data, &sig.key_name);
+                       hash_ctx.update(&sig.ty.to_be_bytes());
+                       hash_ctx.update(&sig.alg.to_be_bytes());
+                       hash_ctx.update(&sig.labels.to_be_bytes());
+                       hash_ctx.update(&sig.orig_ttl.to_be_bytes());
+                       hash_ctx.update(&sig.expiration.to_be_bytes());
+                       hash_ctx.update(&sig.inception.to_be_bytes());
+                       hash_ctx.update(&sig.key_tag.to_be_bytes());
+                       write_name(&mut hash_ctx, &sig.key_name);
 
                        records.sort_unstable();
 
@@ -92,19 +95,18 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
                                        let signed_name = record.name().trailing_n_labels(sig.labels);
                                        debug_assert!(signed_name.is_some());
                                        if let Some(name) = signed_name {
-                                               signed_data.extend_from_slice(b"\x01*");
-                                               write_name(&mut signed_data, name);
+                                               hash_ctx.update(b"\x01*");
+                                               write_name(&mut hash_ctx, name);
                                        } else { return Err(ValidationError::Invalid); }
                                } else {
-                                       write_name(&mut signed_data, record.name());
+                                       write_name(&mut hash_ctx, record.name());
                                }
-                               signed_data.extend_from_slice(&record.ty().to_be_bytes());
-                               signed_data.extend_from_slice(&1u16.to_be_bytes()); // The INternet class
-                               signed_data.extend_from_slice(&sig.orig_ttl.to_be_bytes());
-                               record.write_u16_len_prefixed_data(&mut signed_data);
+                               hash_ctx.update(&record.ty().to_be_bytes());
+                               hash_ctx.update(&1u16.to_be_bytes()); // The INternet class
+                               hash_ctx.update(&sig.orig_ttl.to_be_bytes());
+                               record.serialize_u16_len_prefixed(&mut hash_ctx);
                        }
 
-                       hash_ctx.update(&signed_data);
                        let hash = hash_ctx.finish();
                        let sig_validation = match sig.alg {
                                8|10 => crypto::rsa::validate_rsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
@@ -175,7 +177,7 @@ where RI: IntoIterator<IntoIter = R>, R: Iterator<Item = &'r RRSig>,
                                ctx.update(&dnskey.alg.to_be_bytes());
                                ctx.update(&dnskey.pubkey);
                                let hash = ctx.finish();
-                               if hash.as_ref() == &ds.digest {
+                               if hash.as_ref() == ds.digest {
                                        validated_dnskeys.push(*dnskey);
                                        break;
                                }
@@ -185,7 +187,7 @@ where RI: IntoIterator<IntoIter = R>, R: Iterator<Item = &'r RRSig>,
 
        let mut found_unsupported_alg = false;
        for sig in sigs {
-               match verify_rrsig(sig, validated_dnskeys.iter().map(|k| *k), records.clone()) {
+               match verify_rrsig(sig, validated_dnskeys.iter().copied(), records.clone()) {
                        Ok(()) => return Ok(sig),
                        Err(ValidationError::UnsupportedAlgorithm) => {
                                // There may be redundant signatures by different keys, where one we don't
@@ -193,6 +195,10 @@ where RI: IntoIterator<IntoIter = R>, R: Iterator<Item = &'r RRSig>,
                                // no more, return UnsupportedAlgorithm
                                found_unsupported_alg = true;
                        },
+                       Err(ValidationError::ValidationCountLimited) => {
+                               debug_assert!(false, "verify_rrsig doesn't internally limit");
+                               return Err(ValidationError::ValidationCountLimited);
+                       },
                        Err(ValidationError::Invalid) => {
                                // If a signature is invalid, just immediately fail, avoiding KeyTrap issues.
                                return Err(ValidationError::Invalid);
@@ -255,8 +261,8 @@ fn resolve_time(time: u32) -> u64 {
 }
 
 fn nsec_ord(a: &str, b: &str) -> Ordering {
-       let mut a_label_iter = a.rsplit(".");
-       let mut b_label_iter = b.rsplit(".");
+       let mut a_label_iter = a.rsplit('.');
+       let mut b_label_iter = b.rsplit('.');
        loop {
                match (a_label_iter.next(), b_label_iter.next()) {
                        (Some(_), None) => return Ordering::Greater,
@@ -269,11 +275,11 @@ fn nsec_ord(a: &str, b: &str) -> Ordering {
                                                (Some(_), None) => return Ordering::Greater,
                                                (None, Some(_)) => return Ordering::Less,
                                                (Some(mut a), Some(mut b)) => {
-                                                       if a >= 'A' as u8 && a <= 'Z' as u8 {
-                                                               a += 'a' as u8 - 'A' as u8;
+                                                       if a.is_ascii_uppercase() {
+                                                               a += b'a' - b'A';
                                                        }
-                                                       if b >= 'A' as u8 && b <= 'Z' as u8 {
-                                                               b += 'a' as u8 - 'A' as u8;
+                                                       if b.is_ascii_uppercase() {
+                                                               b += b'a' - b'A';
                                                        }
                                                        if a != b { return a.cmp(&b); }
                                                },
@@ -319,6 +325,7 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
        let mut latest_inception = 0;
        let mut earliest_expiry = u64::MAX;
        let mut min_ttl = u32::MAX;
+       let mut rrsig_sets_validated = 0;
        'next_zone: while zone == "." || !pending_ds_sets.is_empty() {
                let next_ds_set;
                if let Some((next_zone, ds_set)) = pending_ds_sets.pop() {
@@ -329,6 +336,11 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
                        next_ds_set = None;
                }
 
+               rrsig_sets_validated += 1;
+               if rrsig_sets_validated > MAX_PROOF_STEPS {
+                       return Err(ValidationError::ValidationCountLimited);
+               }
+
                let dnskey_rrsigs = inp.iter()
                        .filter_map(|rr| if let RR::RRSig(sig) = rr { Some(sig) } else { None })
                        .filter(|rrsig| rrsig.name.as_str() == zone && rrsig.ty == DnsKey::TYPE);
@@ -346,16 +358,26 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
                latest_inception = cmp::max(latest_inception, resolve_time(verified_dnskey_rrsig.inception));
                earliest_expiry = cmp::min(earliest_expiry, resolve_time(verified_dnskey_rrsig.expiration));
                min_ttl = cmp::min(min_ttl, verified_dnskey_rrsig.orig_ttl);
+
                for rrsig in inp.iter()
                        .filter_map(|rr| if let RR::RRSig(sig) = rr { Some(sig) } else { None })
                        .filter(move |rrsig| rrsig.key_name.as_str() == zone && rrsig.ty != DnsKey::TYPE)
                {
+                       rrsig_sets_validated += 1;
+                       if rrsig_sets_validated > MAX_PROOF_STEPS {
+                               return Err(ValidationError::ValidationCountLimited);
+                       }
+
                        if !rrsig.name.ends_with(zone) { return Err(ValidationError::Invalid); }
                        let signed_records = inp.iter()
                                .filter(|rr| rr.name() == &rrsig.name && rr.ty() == rrsig.ty);
                        match verify_rrsig(rrsig, dnskeys.clone(), signed_records.clone().collect()) {
                                Ok(()) => {},
                                Err(ValidationError::UnsupportedAlgorithm) => continue,
+                               Err(ValidationError::ValidationCountLimited) => {
+                                       debug_assert!(false, "verify_rrsig doesn't internally limit");
+                                       return Err(ValidationError::ValidationCountLimited);
+                               },
                                Err(ValidationError::Invalid) => {
                                        // If a signature is invalid, just immediately fail, avoiding KeyTrap issues.
                                        return Err(ValidationError::Invalid);
@@ -413,7 +435,7 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
                        .filter(|nsec| nsec.name.ends_with(zone.as_str()));
                for nsec in nsec_search {
                        let name_matches = nsec.name.as_str() == name;
-                       let name_contained = nsec_ord(&nsec.name,  &name) != Ordering::Greater &&
+                       let name_contained = nsec_ord(&nsec.name,  name) != Ordering::Greater &&
                                nsec_ord(&nsec.next_name, name) == Ordering::Greater;
                        if (name_matches && !nsec.types.contains_type(ty)) || name_contained {
                                rrs_needing_non_existence_proofs
@@ -439,7 +461,7 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
                        { continue; }
 
                        let mut hasher = crypto::hash::Hasher::sha1();
-                       write_name(&mut hasher, &name);
+                       write_name(&mut hasher, name);
                        hasher.update(&nsec3.salt);
                        for _ in 0..nsec3.hash_iterations {
                                let res = hasher.finish();
@@ -469,7 +491,7 @@ pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, Valid
                                hash
                        } else { continue };
 
-                       let (start_hash_base32, _) = nsec3.name.split_once(".")
+                       let (start_hash_base32, _) = nsec3.name.split_once('.')
                                .unwrap_or_else(|| { debug_assert!(false); ("", "")});
                        let start_hash = if let Ok(start_hash) = base32::decode(start_hash_base32) {
                                start_hash
@@ -534,7 +556,7 @@ impl<'a> VerifiedRRStream<'a> {
                                continue;
                        }
 
-                       return self.verified_rrs.iter().filter(|rr| rr.name() == name).map(|rr| *rr).collect();
+                       return self.verified_rrs.iter().filter(|rr| rr.name() == name).copied().collect();
                }
        }
 }