use crate::crypto;
use crate::rr::*;
use crate::ser::write_name;
+use crate::MAX_PROOF_STEPS;
/// Gets the trusted root anchors
///
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() {
_ => 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();
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())
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;
}
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
// 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);
}
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,
(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); }
},
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() {
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);
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);
.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
{ 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();
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
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();
}
}
}