1use alloc::borrow::ToOwned;
4use alloc::vec::Vec;
5use alloc::vec;
6use core::cmp::{self, Ordering};
7
8use crate::base32;
9use crate::crypto;
10use crate::rr::*;
11use crate::ser::write_name;
12use crate::unhex::unhex;
13use crate::MAX_PROOF_STEPS;
14
15pub fn root_hints() -> Vec<DS> {
19 #[allow(unused_mut)]
20 let mut res = vec![
21 DS {
28 name: ".".try_into().unwrap(), key_tag: 20326, alg: 8, digest_type: 2,
29 digest: unhex::<32>("E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D").to_vec(),
30 }, DS {
31 name: ".".try_into().unwrap(), key_tag: 38696, alg: 8, digest_type: 2,
32 digest: unhex::<32>("683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16").to_vec(),
33 }];
34 #[cfg(test)]
36 res.push(DS {
37 name: ".".try_into().unwrap(), key_tag: 47005, alg: 13, digest_type: 2,
38 digest: unhex::<32>("2eb6e9f2480126691594d649a5a613de3052e37861634641bb568746f2ffc4d4").to_vec(),
39 });
40 res
41}
42
43#[derive(Debug, PartialEq)]
44pub enum ValidationError {
46 UnsupportedAlgorithm,
52 Invalid,
54 ValidationCountLimited,
57}
58
59fn verify_rrsig<'a, RR: WriteableRecord, Keys>(sig: &RRSig, dnskeys: Keys, mut records: Vec<&RR>)
60-> Result<(), ValidationError>
61where Keys: IntoIterator<Item = &'a DnsKey> {
62 for record in records.iter() {
63 if sig.ty != record.ty() { return Err(ValidationError::Invalid); }
64 }
65 for dnskey in dnskeys.into_iter() {
66 if dnskey.key_tag() == sig.key_tag {
67 if dnskey.protocol != 3 { continue; }
69 if dnskey.flags & 0b1_0000_0000 == 0 { continue; }
71 if dnskey.flags & 0b0_1000_0000 != 0 { continue; }
73 if dnskey.alg != sig.alg { continue; }
74
75 let mut hash_ctx = match sig.alg {
76 8 => crypto::hash::Hasher::sha256(),
77 10 => crypto::hash::Hasher::sha512(),
78 13 => crypto::hash::Hasher::sha256(),
79 14 => crypto::hash::Hasher::sha384(),
80 15 => crypto::hash::Hasher::sha512(),
81 _ => return Err(ValidationError::UnsupportedAlgorithm),
82 };
83
84 hash_ctx.update(&sig.ty.to_be_bytes());
85 hash_ctx.update(&sig.alg.to_be_bytes());
86 hash_ctx.update(&sig.labels.to_be_bytes());
87 hash_ctx.update(&sig.orig_ttl.to_be_bytes());
88 hash_ctx.update(&sig.expiration.to_be_bytes());
89 hash_ctx.update(&sig.inception.to_be_bytes());
90 hash_ctx.update(&sig.key_tag.to_be_bytes());
91 write_name(&mut hash_ctx, &sig.key_name);
92
93 records.sort_unstable();
94
95 records.dedup();
99
100 for record in records.iter() {
101 let record_labels = record.name().labels() as usize;
102 let labels = sig.labels.into();
103 if record.ty() != NSec::TYPE && record_labels != labels {
108 if record_labels < labels { return Err(ValidationError::Invalid); }
109 let signed_name = record.name().trailing_n_labels(sig.labels);
110 debug_assert!(signed_name.is_some());
111 if let Some(name) = signed_name {
112 hash_ctx.update(b"\x01*");
113 write_name(&mut hash_ctx, name);
114 } else { return Err(ValidationError::Invalid); }
115 } else {
116 write_name(&mut hash_ctx, record.name());
117 }
118 hash_ctx.update(&record.ty().to_be_bytes());
119 hash_ctx.update(&1u16.to_be_bytes()); hash_ctx.update(&sig.orig_ttl.to_be_bytes());
121 record.serialize_u16_len_prefixed(&mut hash_ctx);
122 }
123
124 let hash = hash_ctx.finish();
125 let sig_validation = match sig.alg {
126 8|10 => crypto::rsa::validate_rsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
127 .map_err(|_| ValidationError::Invalid),
128 13 => crypto::secp256r1::validate_ecdsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
129 .map_err(|_| ValidationError::Invalid),
130 14 => crypto::secp384r1::validate_ecdsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
131 .map_err(|_| ValidationError::Invalid),
132 _ => return Err(ValidationError::UnsupportedAlgorithm),
134 };
135 #[cfg(fuzzing)] {
136 if sig.signature.get(0) == Some(&1) {
139 return Ok(());
140 }
141 }
142
143 sig_validation?;
149
150 return Ok(());
151 }
152 }
153 Err(ValidationError::Invalid)
154}
155
156fn verify_dnskeys<'r, 'd, RI, R, DI, D>(sigs: RI, dses: DI, records: Vec<&DnsKey>)
158-> Result<&'r RRSig, ValidationError>
159where RI: IntoIterator<IntoIter = R>, R: Iterator<Item = &'r RRSig>,
160 DI: IntoIterator<IntoIter = D>, D: Iterator<Item = &'d DS> + Clone {
161 let mut validated_dnskeys = Vec::with_capacity(records.len());
162 let dses = dses.into_iter();
163
164 let mut had_known_digest_type = false;
165 let mut had_ds = false;
166 for ds in dses.clone() {
167 had_ds = true;
168 if ds.digest_type == 1 || ds.digest_type == 2 || ds.digest_type == 4 {
169 had_known_digest_type = true;
170 break;
171 }
172 }
173 if !had_ds { return Err(ValidationError::Invalid); }
174 if !had_known_digest_type { return Err(ValidationError::UnsupportedAlgorithm); }
175
176 for dnskey in records.iter() {
177 let trust_sha1 = dses.clone().all(|ds| ds.digest_type != 2 && ds.digest_type != 4);
179 for ds in dses.clone() {
180 if ds.alg != dnskey.alg { continue; }
181 if dnskey.key_tag() == ds.key_tag {
182 let mut ctx = match ds.digest_type {
183 1 if trust_sha1 => crypto::hash::Hasher::sha1(),
184 2 => crypto::hash::Hasher::sha256(),
185 4 => crypto::hash::Hasher::sha384(),
186 _ => continue,
187 };
188 write_name(&mut ctx, &dnskey.name);
189 ctx.update(&dnskey.flags.to_be_bytes());
190 ctx.update(&dnskey.protocol.to_be_bytes());
191 ctx.update(&dnskey.alg.to_be_bytes());
192 ctx.update(&dnskey.pubkey);
193 let hash = ctx.finish();
194 if hash.as_ref() == ds.digest {
195 validated_dnskeys.push(*dnskey);
196 break;
197 }
198 }
199 }
200 }
201
202 let mut found_unsupported_alg = false;
203 for sig in sigs {
204 if !validated_dnskeys.iter().any(|key| key.key_tag() == sig.key_tag) {
205 continue;
209 }
210 match verify_rrsig(sig, validated_dnskeys.iter().copied(), records.clone()) {
211 Ok(()) => return Ok(sig),
212 Err(ValidationError::UnsupportedAlgorithm) => {
213 found_unsupported_alg = true;
217 },
218 Err(ValidationError::ValidationCountLimited) => {
219 debug_assert!(false, "verify_rrsig doesn't internally limit");
220 return Err(ValidationError::ValidationCountLimited);
221 },
222 Err(ValidationError::Invalid) => {
223 return Err(ValidationError::Invalid);
225 },
226 }
227 }
228
229 if found_unsupported_alg {
230 Err(ValidationError::UnsupportedAlgorithm)
231 } else {
232 Err(ValidationError::Invalid)
233 }
234}
235
236#[derive(Debug, Clone)]
242pub struct VerifiedRRStream<'a> {
243 pub verified_rrs: Vec<&'a RR>,
249 pub valid_from: u64,
257 pub expires: u64,
265 pub max_cache_ttl: u32,
270}
271
272fn resolve_time(time: u32) -> u64 {
273 if time < 60*60*24*365*27 {
277 (time as u64) + (u32::MAX as u64)
278 } else {
279 time.into()
280 }
281}
282
283fn nsec_ord(a: &[u8], b: &[u8]) -> Ordering {
284 let mut a_label_iter = a.rsplit(|c| *c == b'.');
285 let mut b_label_iter = b.rsplit(|c| *c == b'.');
286 loop {
287 match (a_label_iter.next(), b_label_iter.next()) {
288 (Some(_), None) => return Ordering::Greater,
289 (None, Some(_)) => return Ordering::Less,
290 (Some(a_label), Some(b_label)) => {
291 let mut a_bytes = a_label.iter().copied();
292 let mut b_bytes = b_label.iter().copied();
293 loop {
294 match (a_bytes.next(), b_bytes.next()) {
295 (Some(_), None) => return Ordering::Greater,
296 (None, Some(_)) => return Ordering::Less,
297 (Some(mut a), Some(mut b)) => {
298 if a.is_ascii_uppercase() {
299 a += b'a' - b'A';
300 }
301 if b.is_ascii_uppercase() {
302 b += b'a' - b'A';
303 }
304 if a != b { return a.cmp(&b); }
305 },
306 (None, None) => break,
307 }
308 }
309 },
310 (None, None) => return Ordering::Equal,
311 }
312 }
313}
314fn nsec_ord_extra<T>(a: &(&str, T), b: &(&str, T)) -> Ordering {
315 nsec_ord(a.0.as_bytes(), b.0.as_bytes())
316}
317
318#[cfg(test)]
319#[test]
320fn rfc4034_sort_test() {
321 let v = vec![&b"example."[..], &b"a.example."[..], &b"yljkjljk.a.example."[..],
323 &b"Z.a.example."[..], &b"zABC.a.EXAMPLE."[..], &b"z.example."[..],
324 &b"\001.z.example."[..], &b"*.z.example."[..], &b"\xc8.z.example."[..]];
325 let mut sorted = v.clone();
326 sorted.sort_unstable_by(|a, b| nsec_ord(a, b));
327 assert_eq!(sorted, v);
328}
329
330pub fn verify_rr_stream<'a>(inp: &'a [RR]) -> Result<VerifiedRRStream<'a>, ValidationError> {
341 let mut zone = ".";
342 let mut res = Vec::new();
343 let mut rrs_needing_non_existence_proofs = Vec::new();
344 let mut nsec_records = Vec::new();
345 let mut pending_ds_sets = Vec::with_capacity(1);
346 let mut latest_inception = 0;
347 let mut earliest_expiry = u64::MAX;
348 let mut min_ttl = u32::MAX;
349 let mut rrsig_sets_validated = 0;
350 'next_zone: while zone == "." || !pending_ds_sets.is_empty() {
351 let next_ds_set;
352 if let Some((next_zone, ds_set)) = pending_ds_sets.pop() {
353 next_ds_set = Some(ds_set);
354 zone = next_zone;
355 } else {
356 debug_assert_eq!(zone, ".");
357 next_ds_set = None;
358 }
359
360 rrsig_sets_validated += 1;
361 if rrsig_sets_validated > MAX_PROOF_STEPS {
362 return Err(ValidationError::ValidationCountLimited);
363 }
364
365 let dnskey_rrsigs = inp.iter()
366 .filter_map(|rr| if let RR::RRSig(sig) = rr { Some(sig) } else { None })
367 .filter(|rrsig| rrsig.name.as_str() == zone && rrsig.ty == DnsKey::TYPE);
368 let dnskeys = inp.iter()
369 .filter_map(|rr| if let RR::DnsKey(dnskey) = rr { Some(dnskey) } else { None })
370 .filter(move |dnskey| dnskey.name.as_str() == zone);
371 let root_hints = root_hints();
372 let verified_dnskey_rrsig = if zone == "." {
373 verify_dnskeys(dnskey_rrsigs, &root_hints, dnskeys.clone().collect())?
374 } else {
375 debug_assert!(next_ds_set.is_some());
376 if next_ds_set.is_none() { break 'next_zone; }
377 verify_dnskeys(dnskey_rrsigs, next_ds_set.clone().unwrap(), dnskeys.clone().collect())?
378 };
379 latest_inception = cmp::max(latest_inception, resolve_time(verified_dnskey_rrsig.inception));
380 earliest_expiry = cmp::min(earliest_expiry, resolve_time(verified_dnskey_rrsig.expiration));
381 min_ttl = cmp::min(min_ttl, verified_dnskey_rrsig.orig_ttl);
382
383 for rrsig in inp.iter()
384 .filter_map(|rr| if let RR::RRSig(sig) = rr { Some(sig) } else { None })
385 .filter(move |rrsig| rrsig.key_name.as_str() == zone && rrsig.ty != DnsKey::TYPE)
386 {
387 rrsig_sets_validated += 1;
388 if rrsig_sets_validated > MAX_PROOF_STEPS {
389 return Err(ValidationError::ValidationCountLimited);
390 }
391
392 if !rrsig.name.ends_with_labels(zone) { return Err(ValidationError::Invalid); }
393 let signed_records = inp.iter()
394 .filter(|rr| rr.name() == &rrsig.name && rr.ty() == rrsig.ty);
395 match verify_rrsig(rrsig, dnskeys.clone(), signed_records.clone().collect()) {
396 Ok(()) => {},
397 Err(ValidationError::UnsupportedAlgorithm) => continue,
398 Err(ValidationError::ValidationCountLimited) => {
399 debug_assert!(false, "verify_rrsig doesn't internally limit");
400 return Err(ValidationError::ValidationCountLimited);
401 },
402 Err(ValidationError::Invalid) => {
403 return Err(ValidationError::Invalid);
405 }
406 }
407 latest_inception = cmp::max(latest_inception, resolve_time(rrsig.inception));
408 earliest_expiry = cmp::min(earliest_expiry, resolve_time(rrsig.expiration));
409 min_ttl = cmp::min(min_ttl, rrsig.orig_ttl);
410 match rrsig.ty {
411 RRSig::TYPE|DnsKey::TYPE => return Err(ValidationError::Invalid),
413 DS::TYPE => {
414 if rrsig.labels != rrsig.name.labels() { continue; }
417 if !pending_ds_sets.iter().any(|(pending_zone, _)| pending_zone == &rrsig.name.as_str()) {
418 pending_ds_sets.push((
419 &rrsig.name,
420 signed_records.filter_map(|rr|
421 if let RR::DS(ds) = rr { Some(ds) }
422 else { debug_assert!(false, "We already filtered by type"); None })
423 ));
424 }
425 },
426 _ => {
427 if rrsig.labels != rrsig.name.labels() && rrsig.ty != NSec::TYPE {
428 if rrsig.ty == NSec3::TYPE {
429 return Err(ValidationError::Invalid);
432 }
433 if rrsig.labels == u8::MAX { return Err(ValidationError::Invalid); }
440 let proof_name = rrsig.name.trailing_n_labels(rrsig.labels + 1)
441 .ok_or(ValidationError::Invalid)?;
442 rrs_needing_non_existence_proofs.push((proof_name, &rrsig.key_name));
443 }
444 for record in signed_records {
445 if !res.contains(&record) {
446 if record.ty() == NSec::TYPE || record.ty() == NSec3::TYPE {
447 nsec_records.push((record, &rrsig.key_name));
448 }
449 res.push(record);
450 }
451 }
452 },
453 }
454 }
455 continue 'next_zone;
456 }
457 if res.is_empty() { return Err(ValidationError::Invalid) }
458 if latest_inception >= earliest_expiry { return Err(ValidationError::Invalid) }
459
460 rrs_needing_non_existence_proofs.sort_unstable_by(nsec_ord_extra);
462 'proof_search_loop: while let Some((name, zone)) = rrs_needing_non_existence_proofs.pop() {
463 let local_zone_nsecs =
464 nsec_records.iter().filter(|(_, nsec_zone)| *nsec_zone == zone).map(|(rr, _)| rr);
465 let nsec_search = local_zone_nsecs.clone()
466 .filter_map(|rr| if let RR::NSec(nsec) = rr { Some(nsec) } else { None });
467 for nsec in nsec_search {
468 if name_ends_with_labels(&nsec.next_name, name) { continue; }
473 let after_start = nsec_ord(nsec.name.as_bytes(), name.as_bytes()) == Ordering::Less;
475 let before_end = nsec_ord(&nsec.next_name, name.as_bytes()) == Ordering::Greater;
476 let name_contained = if nsec_ord(nsec.name.as_bytes(), &nsec.next_name) == Ordering::Less {
477 after_start && before_end
478 } else {
479 after_start || before_end
480 };
481 if name_contained {
482 rrs_needing_non_existence_proofs.retain(|(n, z)| *n != name || *z != zone);
483 continue 'proof_search_loop;
484 }
485 }
486
487 let nsec3_search = local_zone_nsecs.clone()
488 .filter_map(|rr| if let RR::NSec3(nsec3) = rr { Some(nsec3) } else { None });
489 let mut nsec3params_to_name_hash = Vec::new();
491 for nsec3 in nsec3_search.clone() {
492 if nsec3.hash_iterations > 2500 {
493 continue;
496 }
497 if nsec3.hash_algo != 1 { continue; }
498 if nsec3params_to_name_hash.iter()
499 .any(|(iterations, salt, _)| *iterations == nsec3.hash_iterations && *salt == &nsec3.salt)
500 { continue; }
501
502 let mut hasher = crypto::hash::Hasher::sha1();
503 write_name(&mut hasher, name);
504 hasher.update(&nsec3.salt);
505 for _ in 0..nsec3.hash_iterations {
506 let res = hasher.finish();
507 hasher = crypto::hash::Hasher::sha1();
508 hasher.update(res.as_ref());
509 hasher.update(&nsec3.salt);
510 }
511 nsec3params_to_name_hash.push((nsec3.hash_iterations, &nsec3.salt, hasher.finish()));
512
513 if nsec3params_to_name_hash.len() >= 2 {
514 break;
517 }
518 }
519 for nsec3 in nsec3_search {
520 if nsec3.flags != 0 {
521 continue;
524 }
525 if nsec3.hash_algo != 1 { continue; }
526 let name_hash = if let Some((_, _, hash)) =
527 nsec3params_to_name_hash.iter()
528 .find(|(iterations, salt, _)| *iterations == nsec3.hash_iterations && *salt == &nsec3.salt)
529 {
530 hash
531 } else { continue };
532
533 let (start_hash_base32, _) = nsec3.name.split_once('.')
534 .unwrap_or_else(|| { debug_assert!(false); ("", "")});
535 let start_hash = if let Ok(start_hash) = base32::decode(start_hash_base32) {
536 start_hash
537 } else { continue };
538 if start_hash.len() != 20 || nsec3.next_name_hash.len() != 20 { continue; }
539
540 let after_start = &start_hash[..] < name_hash.as_ref();
542 let before_end = &nsec3.next_name_hash[..] > name_hash.as_ref();
543 let hash_contained = if start_hash[..] < nsec3.next_name_hash[..] {
544 after_start && before_end
545 } else {
546 after_start || before_end
547 };
548 if hash_contained {
549 rrs_needing_non_existence_proofs.retain(|(n, z)| *n != name || *z != zone);
550 continue 'proof_search_loop;
551 }
552 }
553 return Err(ValidationError::Invalid);
554 }
555
556 res.retain(|rr| rr.ty() != NSec::TYPE && rr.ty() != NSec3::TYPE);
557
558 Ok(VerifiedRRStream {
559 verified_rrs: res, valid_from: latest_inception, expires: earliest_expiry,
560 max_cache_ttl: min_ttl,
561 })
562}
563
564impl<'a> VerifiedRRStream<'a> {
565 pub fn resolve_name<'b>(&self, name_param: &'b Name) -> Vec<&'a RR> where 'a: 'b {
575 let mut dname_name;
576 let mut name = name_param;
577 for _ in 0..MAX_PROOF_STEPS {
578 let mut cname_search = self.verified_rrs.iter()
579 .filter(|rr| rr.name() == name)
580 .filter_map(|rr| if let RR::CName(cn) = rr { Some(cn) } else { None });
581 if let Some(cname) = cname_search.next() {
582 name = &cname.canonical_name;
583 continue;
584 }
585
586 let mut dname_search = self.verified_rrs.iter()
587 .filter(|rr| name.len() > rr.name().len() && name.ends_with_labels(&**rr.name()))
588 .filter_map(|rr| if let RR::DName(dn) = rr { Some(dn) } else { None });
589 if let Some(dname) = dname_search.next() {
590 let prefix = name.strip_suffix(&*dname.name).expect("We just filtered for this");
591 let resolved_name = if dname.delegation_name.as_str() == "." {
592 prefix.to_owned()
593 } else {
594 prefix.to_owned() + &dname.delegation_name
595 };
596 dname_name = if let Ok(name) = resolved_name.try_into() {
597 name
598 } else {
599 return Vec::new();
601 };
602 name = &dname_name;
603 continue;
604 }
605
606 return self.verified_rrs.iter().filter(|rr| rr.name() == name).copied().collect();
607 }
608 Vec::new()
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 #![allow(deprecated)]
615
616 use super::*;
617
618 use alloc::borrow::ToOwned;
619
620 use crate::ser::{parse_rr_stream, write_rr};
621
622 use hex_conservative::FromHex;
623 use rand::seq::SliceRandom;
624
625 fn root_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
626 let dnskeys = vec![DnsKey {
627 name: ".".try_into().unwrap(), flags: 256, protocol: 3, alg: 8,
628 pubkey: base64::decode("AwEAAeCYD6Z7WWKVLeuWgowKP+3g+Gs1cnLKq7a3CaQxQpv8bfuFVI0WnG33qaSH/Mw9IBgifrdzf4XY/DQLnyBJ9MfaOyAWuEaEmYJ+GQPiwVVfstGwSA1McfFJUttTgq2Huu74KARhtA8wPo/N3XcyYQtNhz+qCM5NBb3ecx/naw6sYab9LxS6f2cU0q03++BP5Ks0Uef8WJCa/1izCYE+vMkwoltV+tENa3hpXiZ7jle/xdgaZrPi5ZGmyLVI34g1XVYrNlsCCTmNvFQIfzW5STFQFsQpizczyFn9r3LzSxxPCNwdlCG84bER0BmdwqbF6Tanv+FxMOavrahkj4wIy5k=").unwrap(),
629 }, DnsKey {
630 name: ".".try_into().unwrap(), flags: 257, protocol: 3, alg: 8,
631 pubkey: base64::decode("AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU=").unwrap(),
632 }, DnsKey {
633 name: ".".try_into().unwrap(), flags: 257, protocol: 3, alg: 8,
634 pubkey: base64::decode("AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc=").unwrap(),
635 }];
636 let dnskey_rrsig = RRSig {
637 name: ".".try_into().unwrap(), ty: DnsKey::TYPE, alg: 8, labels: 0, orig_ttl: 172800,
638 expiration: 1787270400, inception: 1785456000, key_tag: 20326, key_name: ".".try_into().unwrap(),
639 signature: base64::decode("kKpaPEyKDhNNs9rizpyOEcK2Nwg04lOQEeUgL175fR1W7zTzCo96Lpf5xuMeN9vZKHq362Yi6pX11BeCL5JejRJ0q4OflNu2jkjZpqW4JWNeim0gaJxd2fg8MMh7qg9gZnMfFLzK8y2Hudve+JF3LD/MjIPpdPd/GEISqynmOLYxNmol2eKMdiDJ3RNxVTfzmEo8QqXGGDEozWf5pM9XvTa22LLJ37NHblwT0eeHnhxKwt5q4RH7YabmC2NlUge5R/GoCQD0WJdmi139O7DyyRDa1YEHSPigdLDRjXuAmw1acBrLbmvS7F9j1Zbc2Mm1MO924z6z9L3GXPlaSV1NJw==").unwrap(),
640 };
641 let root_hints = root_hints();
642 verify_dnskeys([&dnskey_rrsig], &root_hints, dnskeys.iter().collect()).unwrap();
643 let rrs = vec![dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskeys[2].clone().into(), dnskey_rrsig.into()];
644 (dnskeys, rrs)
645 }
646
647 fn com_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
648 let root_dnskeys = root_dnskey().0;
649 let mut com_ds = vec![DS {
650 name: "com.".try_into().unwrap(), key_tag: 19718, alg: 13, digest_type: 2,
651 digest: Vec::from_hex("8ACBB0CD28F41250A80A491389424D341522D946B0DA0C0291F2D3D771D7805A").unwrap(),
652 }];
653 let ds_rrsig = RRSig {
654 name: "com.".try_into().unwrap(), ty: DS::TYPE, alg: 8, labels: 1, orig_ttl: 86400,
655 expiration: 1787115600, inception: 1785988800, key_tag: 57780, key_name: ".".try_into().unwrap(),
656 signature: base64::decode("n8/OgzlB4FN+3Qmdv+U7tVrHXIRumf2fqRXun1TQW4GUwQr7RMFLiYuymzog8chIA+7RsIkTMTNmV2FezFqGWtl2/Kp1PSTtdEKgziLDtXz2em0KZmvoOdm/uH+37wRtgK91HdVXpKKhESp4zGYZKBQP1oMZQ4hsCHndZrIh7sHgmp7b7lypbqrPFSlDm71VZsYpEXKF+3nD3CUD3S0spCHkS5StcXNfxZGDyr/UBX3/gYWPyX9VU3zcHfShOckX+QLIKqtbbXzM8/LjMwZHnZdehv156JQzOJRoGGrhDlKZ6L5C5PPXVRi6JthP+bwGRFWeJ9/yyWbH6o/RtUJdLg==").unwrap(),
657 };
658 verify_rrsig(&ds_rrsig, &root_dnskeys, com_ds.iter().collect()).unwrap();
659 let dnskeys = vec![DnsKey {
660 name: "com.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
661 pubkey: base64::decode("o6onp+t66olg3PFhuIXaaatogT3OrqHrqt48IkYGuxwW8tSnQVMGyO+TSmp8sTRtkD9km+N/VQvvtqVM8ra9/Q==").unwrap(),
662 }, DnsKey {
663 name: "com.".try_into().unwrap(), flags: 257, protocol: 3, alg: 13,
664 pubkey: base64::decode("tx8EZRAd2+K/DJRV0S+hbBzaRPS/G6JVNBitHzqpsGlz8huE61Ms9ANe6NSDLKJtiTBqfTJWDAywEp1FCsEINQ==").unwrap(),
665 }];
666 let dnskey_rrsig = RRSig {
667 name: "com.".try_into().unwrap(), ty: DnsKey::TYPE, alg: 13, labels: 1, orig_ttl: 86400,
668 expiration: 1787234555, inception: 1785938255, key_tag: 19718, key_name: "com.".try_into().unwrap(),
669 signature: base64::decode("+mh5ldazL68oULmaud6VQrHXYpcjbUZ8sJLBlc8HWf4jaB7TXCSsnUCQhxpZ9wtemVxj7TptSGsAhHXEvrgpvw==").unwrap(),
670 };
671 verify_dnskeys([&dnskey_rrsig], &com_ds, dnskeys.iter().collect()).unwrap();
672 let rrs = vec![com_ds.pop().unwrap().into(), ds_rrsig.into(),
673 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskey_rrsig.into()];
674 (dnskeys, rrs)
675 }
676
677 fn ninja_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
678 let root_dnskeys = root_dnskey().0;
679 let mut ninja_ds = vec![DS {
680 name: "ninja.".try_into().unwrap(), key_tag: 46082, alg: 8, digest_type: 2,
681 digest: Vec::from_hex("C8F816A7A575BDB2F997F682AAB2653BA2CB5EDDB69B036A30742A33BEFAF141").unwrap(),
682 }];
683 let ds_rrsig = RRSig {
684 name: "ninja.".try_into().unwrap(), ty: DS::TYPE, alg: 8, labels: 1, orig_ttl: 86400,
685 expiration: 1787115600, inception: 1785988800, key_tag: 57780, key_name: ".".try_into().unwrap(),
686 signature: base64::decode("tCn+vWmRGtTe0aAIajh5+u0FDgs4u7+RLIkXyzjoE5RQ1wm9yO2pQDaGAvXZFI/gpu1AI0JRDHg8Wt5Pwcu6ng7UrhhrPEvATfTbPqRXVBix7GGbm7r3RjgNZriSBSJpP9ehiI56Ay/32TN1ejS5LfcLq0kCZX1MTh4vEhjy0vSzaf7f3QiyhUcTeg+1x+o2kVCKbvtsrTWhJXpt0Y5KGN/VaUUlMw80XTK42heYBz0U4OBZVhiYDem9uBozH2q0/8FxbAMqhLUuMcZNq1cSQ23LeuzbpvpjIul4dI3R0p2kCCIZx/weNGjVbsPNBt0FwVqeBCKaD7lUaMAiKBGqpQ==").unwrap(),
687 };
688 verify_rrsig(&ds_rrsig, &root_dnskeys, ninja_ds.iter().collect()).unwrap();
689 let dnskeys = vec![DnsKey {
690 name: "ninja.".try_into().unwrap(), flags: 256, protocol: 3, alg: 8,
691 pubkey: base64::decode("AwEAAcEhG/hFkNTJK5wLP7a1xXzT076x5qG24PPISw5r+JSD9mS0mcaZX+87tmOmuiKPvxAZj14nnMjcHA4J/n2RTJkofPZ+SLASOxg/mngB2trNdFEUhmEbisqE7UOuPQD55C6jee4tJ9Xs0thHEXlN3gO6MJj0+jk17skLJuH4P+td").unwrap(),
692 }, DnsKey {
693 name: "ninja.".try_into().unwrap(), flags: 256, protocol: 3, alg: 8,
694 pubkey: base64::decode("AwEAAc2FxKHX6CerPDnSAS8XKiERe1LU4K6Du1QW8WpR3c7D/6LWLnNQpNbRTZl/NIoAcoQ9sOLl2zvOMUR7KCBpC7DYvmG+Nh8dzwYWlI+rIZj9UUyy2f+9QB6gVMPeqF49vTzibNxeZf7ftiTHKi3ubcMYZdTA4JOdlX6hUL3w7+69").unwrap(),
695 }, DnsKey {
696 name: "ninja.".try_into().unwrap(), flags: 257, protocol: 3, alg: 8,
697 pubkey: base64::decode("AwEAAcceTJ3Ekkmiez70L8uNVrTDrHZxXHrQHEHQ1DJZDRXDxizuSy0prDXy1yybMqcKAkPL0IruvJ9vHg5j2eHN/hM8RVqCQ1wHgLdQASyUL37VtmLuyNmuiFpYmT+njXVh/tzRHZ4cFxrLAtACWDe6YaPApnVkJ0FEcMnKCQaymBaLX02WQOYuG3XdBr5mQQTtMs/kR/oh83QBcSxyCg3KS7G8IPP6MQPK0za94gsW9zlI5rgN2gpSjbU2qViGjDhw7N3PsC37PLTSLirUmkufeMkP9sfhDjAbP7Nv6FmpTDAIRmBmV0HBT/YNBTUBP89DmEDsrYL8knjkrOaLqV5wgkk=").unwrap(),
698 }];
699 let dnskey_rrsig = RRSig {
700 name: "ninja.".try_into().unwrap(), ty: DnsKey::TYPE, alg: 8, labels: 1, orig_ttl: 3600,
701 expiration: 1787414103, inception: 1785596103, key_tag: 46082, key_name: "ninja.".try_into().unwrap(),
702 signature: base64::decode("gx365BoyGgZpy1zqP9Ih8MAx0/SG0e5v1OR7Z7ecXONuufBne0ksny8IoMY7fxEj1Fug/7CJwJMqN1AICPVgK45u9wAPGMlIp552cTaQEw+YIoeOpYaIt5XbQ7xkbVTjoXNkMJQUam1cQrodQzvd5U6AlfoTAPAgWYzVXnbGFOeOjTNqmetf3hz13qDX5zNhG4TJOdOvdLplYXEpGZR+2CMRZTBguuwXEVTWVo9+Cyet+aKNQN0vx9aRanY6PMqbXgNxDFtFlBkqAdaESI2/6gz4QkDDZbG2R357wNqHM82FQdAu2FBSqH4NkhP0OBV6xTJ2EQrmwVsAz7R7rJOk3A==").unwrap(),
703 };
704 verify_dnskeys([&dnskey_rrsig], &ninja_ds, dnskeys.iter().collect()).unwrap();
705 let rrs = vec![ninja_ds.pop().unwrap().into(), ds_rrsig.into(),
706 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskeys[2].clone().into(),
707 dnskey_rrsig.into()];
708 (dnskeys, rrs)
709 }
710
711 fn mattcorallo_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
712 let com_dnskeys = com_dnskey().0;
713 let mattcorallo_ds = vec![DS {
714 name: "mattcorallo.com.".try_into().unwrap(), key_tag: 9033, alg: 13, digest_type: 2,
715 digest: Vec::from_hex("282511C1378832188575A172F29A89C09AC28C826FC4FE78534D4C6DF5EED2F0").unwrap(),
716 }, DS {
717 name: "mattcorallo.com.".try_into().unwrap(), key_tag: 58101, alg: 13, digest_type: 2,
718 digest: Vec::from_hex("F0E161567D468087FF27B051ABC94476178A7CB635DA1AA705E05C77CA81DE52").unwrap(),
719 }];
720 let ds_rrsig = RRSig {
721 name: "mattcorallo.com.".try_into().unwrap(), ty: DS::TYPE, alg: 13, labels: 2, orig_ttl: 86400,
722 expiration: 1786415920, inception: 1785806920, key_tag: 41446, key_name: "com.".try_into().unwrap(),
723 signature: base64::decode("e0j29NIzuHAvtSKf04LKShm2vVOiwkKlluoXyTsq9yB+prqyJ/RTU4Na/ZBHuH0ygnQUET6C5SEaQuM5gUd9+w==").unwrap(),
724 };
725 verify_rrsig(&ds_rrsig, &com_dnskeys, mattcorallo_ds.iter().collect()).unwrap();
726 let dnskeys = vec![DnsKey {
727 name: "mattcorallo.com.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
728 pubkey: base64::decode("eEAgU/iS8VR7ubg5lArqTACdBHxK8ERx5TpTWCw9wc25pe2JiN0/iN3QgfmOBs6AUpVu+iF36abdUdct/TRLjQ==").unwrap(),
729 }, DnsKey {
730 name: "mattcorallo.com.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
731 pubkey: base64::decode("yPAeYPanlAxAHZ9rb7LAqP2LrTZYVhECybfwXqn84b1kvhtBCS22I++mTIcYd681BKwv6WazOi03h8se5mK/KA==").unwrap(),
732 }, DnsKey {
733 name: "mattcorallo.com.".try_into().unwrap(), flags: 257, protocol: 3, alg: 13,
734 pubkey: base64::decode("yN2riWFvCTElBchzLyt0U1RjCcXW+evRcq7Ap5EU6gOacleOXft49H2oQDcRqK6C/dJDPbZ52EB5C1WlIYDY4Q==").unwrap(),
735 }];
736 let dnskey_rrsig = RRSig {
737 name: "mattcorallo.com.".try_into().unwrap(), ty: DnsKey::TYPE, alg: 13, labels: 2, orig_ttl: 604800,
738 expiration: 1787071850, inception: 1785856850, key_tag: 9033, key_name: "mattcorallo.com.".try_into().unwrap(),
739 signature: base64::decode("njvuObzGh/mdUjX5mmJKI+hw7ByPlKe6OKI5m/zhQloLXEg9DUxgS2ShV+twWZuuun5x7RXs1FzEIHxf8W6ikQ==").unwrap(),
740 };
741 verify_dnskeys([&dnskey_rrsig], &mattcorallo_ds, dnskeys.iter().collect()).unwrap();
742 let rrs = vec![mattcorallo_ds[0].clone().into(), mattcorallo_ds[1].clone().into(),
743 ds_rrsig.into(),
744 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskeys[2].clone().into(),
745 dnskey_rrsig.into()];
746 (dnskeys, rrs)
747 }
748
749 fn mattcorallo_txt_record() -> (Vec<Txt>, RRSig) {
750 let txts = vec![Txt {
751 name: "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap(),
752 data: "as long as it doesn't start with bitcoin:, other records should be ignored".try_into().unwrap(),
753 }, Txt {
754 name: "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap(),
755 data: "bitcoin:bc1qztwy6xen3zdtt7z0vrgapmjtfz8acjkfp5fp7l?lno=lno1zr5qyugqgskrk70kqmuq7v3dnr2fnmhukps9n8hut48vkqpqnskt2svsqwjakp7k6pyhtkuxw7y2kqmsxlwruhzqv0zsnhh9q3t9xhx39suc6qsr07ekm5esdyum0w66mnx8vdquwvp7dp5jp7j3v5cp6aj0w329fnkqqv60q96sz5nkrc5r95qffx002q53tqdk8x9m2tmt85jtpmcycvfnrpx3lr45h2g7na3sec7xguctfzzcm8jjqtj5ya27te60j03vpt0vq9tm2n9yxl2hngfnmygesa25s4u4zlxewqpvp94xt7rur4rhxunwkthk9vly3lm5hh0pqv4aymcqejlgssnlpzwlggykkajp7yjs5jvr2agkyypcdlj280cy46jpynsezrcj2kwa2lyr8xvd6lfkph4xrxtk2xc3lpq".try_into().unwrap(),
756 }];
757 let txt_rrsig = RRSig {
758 name: "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap(),
759 ty: Txt::TYPE, alg: 13, labels: 5, orig_ttl: 3600, expiration: 1787055757,
760 inception: 1785840757, key_tag: 41141, key_name: "mattcorallo.com.".try_into().unwrap(),
761 signature: base64::decode("u1Sl6uNJvSZcxoEsew6/MM7GI8smdPkuDSS26t8tEiFece6d8ezI6wcalSbkLKObXeUmdbW5lHZjnBKkUck3MA==").unwrap(),
762 };
763 (txts, txt_rrsig)
764 }
765
766 fn cloudflare_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
767 let com_dnskeys = com_dnskey().0;
768 let mut cloudflare_ds = vec![DS {
769 name: "cloudflare.com.".try_into().unwrap(), key_tag: 2371, alg: 13, digest_type: 2,
770 digest: Vec::from_hex("32996839A6D808AFE3EB4A795A0E6A7A39A76FC52FF228B22B76F6D63826F2B9").unwrap(),
771 }];
772 let ds_rrsig = RRSig {
773 name: "cloudflare.com.".try_into().unwrap(), ty: DS::TYPE, alg: 13, labels: 2, orig_ttl: 86400,
774 expiration: 1786582833, inception: 1785973833, key_tag: 41446, key_name: "com.".try_into().unwrap(),
775 signature: base64::decode("bj58c8zJI4N/l6M0CzR5/ANQTZBlUAzuxeBcz1Nt4M2B2UPM3LoYXQ8ASYZli5caWIkVq2B2kHs0N3dxmu2sUg==").unwrap(),
776 };
777 verify_rrsig(&ds_rrsig, &com_dnskeys, cloudflare_ds.iter().collect()).unwrap();
778 let dnskeys = vec![DnsKey {
779 name: "cloudflare.com.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
780 pubkey: base64::decode("oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==").unwrap(),
781 }, DnsKey {
782 name: "cloudflare.com.".try_into().unwrap(), flags: 257, protocol: 3, alg: 13,
783 pubkey: base64::decode("mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==").unwrap(),
784 }];
785 let dnskey_rrsig = RRSig {
786 name: "cloudflare.com.".try_into().unwrap(), ty: DnsKey::TYPE, alg: 13, labels: 2, orig_ttl: 3600,
787 expiration: 1790916935, inception: 1785646535, key_tag: 2371, key_name: "cloudflare.com.".try_into().unwrap(),
788 signature: base64::decode("14eL8GoAJFkezWYWQr6pcVSq55HRaaykMGstMqOW1bHXDb3Ll0gYj79po1sBCvqUAFEYXZmIBVcYfSytQ63MPw==").unwrap(),
789 };
790 verify_dnskeys([&dnskey_rrsig], &cloudflare_ds, dnskeys.iter().collect()).unwrap();
791 let rrs = vec![cloudflare_ds.pop().unwrap().into(), ds_rrsig.into(),
792 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskey_rrsig.into()];
793 (dnskeys, rrs)
794 }
795
796 fn cloudflare_online_signed_nsec() -> (NSec, RRSig) {
797 let nsec = NSec {
798 name: "online_signing_test.cloudflare.com.".try_into().unwrap(),
799 next_name: b"\x00.online_signing_test.cloudflare.com.".to_vec(),
800 types: NSecTypeMask::from_types(&[RRSig::TYPE, NSec::TYPE, 128]),
801 };
802 let nsec_rrsig = RRSig {
803 name: "online_signing_test.cloudflare.com.".try_into().unwrap(),
804 ty: NSec::TYPE, alg: 13, labels: 3, orig_ttl: 300, expiration: 1786137946,
805 inception: 1785957946, key_tag: 34505, key_name: "cloudflare.com.".try_into().unwrap(),
806 signature: base64::decode("wUIukIrWxEzb0SJOTEC7Kg7e/SbDzIIGZMWfOS7/IpToUYGuJj1Io13ufs5shP4MnfAAWxrfFLa6asyurF0OFg==").unwrap(),
807 };
808 (nsec, nsec_rrsig)
809 }
810
811 fn bitcoin_ninja_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
812 let ninja_dnskeys = ninja_dnskey().0;
813 let bitcoin_ninja_ds = vec![DS {
814 name: "bitcoin.ninja.".try_into().unwrap(), key_tag: 29036, alg: 13, digest_type: 2,
815 digest: Vec::from_hex("3F7AD5A303E9C1CD1474B8DF2AE56F3F82DA8637CA55DB4D9A2BB85960CA698E").unwrap(),
816 }, DS {
817 name: "bitcoin.ninja.".try_into().unwrap(), key_tag: 30142, alg: 13, digest_type: 2,
818 digest: Vec::from_hex("FB445ADFE3314DAAE4884B53592BED42DF1DD0B147B5930E8B16AF2AEAE94504").unwrap(),
819 }];
820 let ds_rrsig = RRSig {
821 name: "bitcoin.ninja.".try_into().unwrap(), ty: DS::TYPE, alg: 8, labels: 2, orig_ttl: 3600,
822 expiration: 1787414103, inception: 1785596103, key_tag: 19731, key_name: "ninja.".try_into().unwrap(),
823 signature: base64::decode("n1wGkPRW8dbBvCD55EAMHR4WaZAxcXJW9Se8xUFr/D3qtJt6ydH5X/+9TqQMvgU/LYcWc0FeIKun5pTLzbDOkufEYfbE3tu+diI9y53zw6qjj0i2NRCj4BdyQletGVDqFfBKQIMs8lrwa+8eqm0lut30iOvu8FyS5RpyISwMkLw=").unwrap(),
824 };
825 verify_rrsig(&ds_rrsig, &ninja_dnskeys, bitcoin_ninja_ds.iter().collect()).unwrap();
826 let dnskeys = vec![DnsKey {
827 name: "bitcoin.ninja.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
828 pubkey: base64::decode("jQGaU+vc1XEIa4507tYhI7tVsKUp+Bd3zdtNMVVxIWSW0QJlJvsXBLnsw+vLHL9HYtYN68ECo1eInYfYk1fILg==").unwrap(),
829 }, DnsKey {
830 name: "bitcoin.ninja.".try_into().unwrap(), flags: 257, protocol: 3, alg: 13,
831 pubkey: base64::decode("T0XjCLwz4dUK2iOKsFAo3j2CW0nEumfvpA5w6gKAAujcD11ewT/6+aRIejB64LG2HGw6J14wiRvxq7uREj972g==").unwrap(),
832 }];
833 let dnskey_rrsig = RRSig {
834 name: "bitcoin.ninja.".try_into().unwrap(), ty: DnsKey::TYPE, alg: 13, labels: 2, orig_ttl: 604800,
835 expiration: 1786846836, inception: 1785631836, key_tag: 30142, key_name: "bitcoin.ninja.".try_into().unwrap(),
836 signature: base64::decode("3jrUWhXokJjCCqhUbE6pac2cYHhW+gjZToZh7MaEWz+avKFHh8gC4BleAcrLUCAspemmNbGPU2ZMhW6L9w4o0w==").unwrap(),
837 };
838 verify_dnskeys([&dnskey_rrsig], &bitcoin_ninja_ds, dnskeys.iter().collect()).unwrap();
839 let rrs = vec![bitcoin_ninja_ds[0].clone().into(), bitcoin_ninja_ds[1].clone().into(),
840 ds_rrsig.into(),
841 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskey_rrsig.into()];
842 (dnskeys, rrs)
843 }
844
845 fn bitcoin_ninja_txt_record() -> (Txt, RRSig) {
846 let txt_resp = Txt {
847 name: "txt_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
848 data: "dnssec_prover_test".try_into().unwrap(),
849 };
850 let txt_rrsig = RRSig {
851 name: "txt_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
852 ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
853 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
854 signature: base64::decode("MtmxM8RekiyS716n4Oz4dmiO32VfHtdLoWVwMPD7o7hwAo3TZoFSi1QOmHEFsrn/wtjghuZTBz/IwVY/3Os1CQ==").unwrap(),
855 };
856 (txt_resp, txt_rrsig)
857 }
858
859 fn bitcoin_ninja_cname_record() -> (CName, RRSig) {
860 let cname_resp = CName {
861 name: "cname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
862 canonical_name: "txt_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
863 };
864 let cname_rrsig = RRSig {
865 name: "cname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
866 ty: CName::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
867 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
868 signature: base64::decode("67U3kvg35HyzESYD4oQ6NtIklzuMUDGkmrP+Bu0Yt0ZWJ249a1+pOMD3IiltgV+kidT6e/17d1HUM7zIKROAJA==").unwrap(),
869 };
870 (cname_resp, cname_rrsig)
871 }
872
873 fn bitcoin_ninja_dname_record() -> (DName, RRSig) {
874 let dname = DName {
875 name: "dname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
876 delegation_name: "cname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
877 };
878 let dname_rrsig = RRSig {
879 name: "dname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
880 ty: DName::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
881 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
882 signature: base64::decode("vxE+K/VVKWpnTPGSDYavj2WTWxCti4TV3KAhzExSaEQBCHbTh7qTptUT33mnOanYtcRV5X46IQRNqZUlHRrDtw==").unwrap(),
883 };
884 (dname, dname_rrsig)
885 }
886
887 fn bitcoin_ninja_dname_sibling_records() -> (Txt, RRSig, Txt, RRSig) {
888 let sibling = Txt {
889 name: "notdname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
890 data: "not_dnamed".try_into().unwrap(),
891 };
892 let sibling_rrsig = RRSig {
893 name: "notdname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
894 ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787258898,
895 inception: 1786043898, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
896 signature: base64::decode("CTnx0YktprRMA14f434Zwyu9NBYxti3NOfIcv5cPCnJxQvU7UmOOuBMtKpnSvvJBKpsbhXfnZCZFazG1Z6fdow==").unwrap(),
897 };
898 let redirected = Txt {
899 name: "notcname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
900 data: "wrongly_dnamed".try_into().unwrap(),
901 };
902 let redirected_rrsig = RRSig {
903 name: "notcname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
904 ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787258898,
905 inception: 1786043898, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
906 signature: base64::decode("uwLsAhkqQHwafsp+mt5X0cwbNXEuIM0zCT+qEJaC65TxS5FNi9PFOS8zo730q70W53OMvDBSkw6xKdf4/mEsSg==").unwrap(),
907 };
908 (sibling, sibling_rrsig, redirected, redirected_rrsig)
909 }
910
911 fn bitcoin_ninja_txt_sort_edge_cases_records() -> (Vec<Txt>, RRSig) {
912 let txts = vec![Txt {
913 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
914 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".try_into().unwrap(),
915 }, Txt {
916 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
917 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".try_into().unwrap(),
918 }, Txt {
919 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
920 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".try_into().unwrap(),
921 }, Txt {
922 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
923 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".try_into().unwrap(),
924 }, Txt {
925 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
926 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaba".try_into().unwrap(),
927 }, Txt {
928 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
929 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".try_into().unwrap(),
930 }, Txt {
931 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
932 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaba".try_into().unwrap(),
933 }, Txt {
934 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
935 data: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaa".try_into().unwrap(),
936 }];
937 let rrsig = RRSig {
938 name: "txt_sort_order.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
939 ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
940 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
941 signature: base64::decode("EllHDC5WHigdu+L4HfKRN3wpXz+lfMLJim+qrIE8DTMvrkiNUFHHLnuayOihrl13MJ5FQPWsX3HgPCKfThvYjg==").unwrap(),
942 };
943 (txts, rrsig)
944 }
945
946 fn bitcoin_ninja_wildcard_record(pfx: &str) -> (Txt, RRSig, NSec3, RRSig) {
948 let name: Name = (pfx.to_owned() + ".wildcard_test.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
949 let txt_resp = Txt {
950 name: name.clone(),
951 data: "wildcard_test".try_into().unwrap(),
952 };
953 let txt_rrsig = RRSig {
954 name: name.clone(),
955 ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
956 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
957 signature: base64::decode("4oXWLvyfOvNunXWIWNEDDTjSKV6N7o8l4DAaZYT9xz0cqfaT2qA0iQ3JeIqwBsKGWVejPHlNhBvdd71Mfo1ZUw==").unwrap(),
958 };
959 let nsec3 = NSec3 {
960 name: "gtcdha57vlrv1q1qrjf7vujvlfajtasp.bitcoin.ninja.".try_into().unwrap(),
961 hash_algo: 1, flags: 0, hash_iterations: 0, salt: Vec::from_hex("057B0C54D4647530").unwrap(),
962 next_name_hash: Vec::from_hex("958BD48D5AED2B834731866E15F2751AE7A920C9").unwrap(),
963 types: NSecTypeMask::from_types(&[16, 46]),
964 };
965 let nsec3_rrsig = RRSig {
966 name: "gtcdha57vlrv1q1qrjf7vujvlfajtasp.bitcoin.ninja.".try_into().unwrap(),
967 ty: NSec3::TYPE, alg: 13, labels: 3, orig_ttl: 60, expiration: 1787229760,
968 inception: 1786014760, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
969 signature: base64::decode("trwirnNf4Z4MKuUGQhD6ylyGF7zeOJdPuJeogKJ0bwH7dr9qcAQy9ZhaZI5TwdDhd2g85JvJ8hLGCjO5LzNCMQ==").unwrap(),
970 };
971 (txt_resp, txt_rrsig, nsec3, nsec3_rrsig)
972 }
973
974 fn bitcoin_ninja_cname_wildcard_record() -> (CName, RRSig, Txt, RRSig, [(NSec3, RRSig); 2]) {
975 let cname_resp = CName {
976 name: "asdf.cname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
977 canonical_name: "cname.wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
978 };
979 let cname_rrsig = RRSig {
980 name: "asdf.cname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
981 ty: CName::TYPE, alg: 13, labels: 4, orig_ttl: 30, expiration: 1787178279,
982 inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
983 signature: base64::decode("zZ/uy4AS7tPB1mjW0qAiRe7eqaI54VOt9MIeaQcjL+xls+OBK9Agtp2t9Z0bmyr5/7wo4l2uIKTFEYQyV4XSRg==").unwrap(),
984 };
985 let nsec3_a = NSec3 {
986 name: "g1ae0emluagb7uci39ts11cgoh4sk2l0.bitcoin.ninja.".try_into().unwrap(),
987 hash_algo: 1, flags: 0, hash_iterations: 0,
988 salt: Vec::from_hex("057B0C54D4647530").unwrap(),
989 next_name_hash: Vec::from_hex("832AF5BE3608C56209E4B83FD341299A4269854C").unwrap(),
990 types: NSecTypeMask::from_types(&[1, 28, 46]),
991 };
992 let nsec3_a_rrsig = RRSig {
993 name: "g1ae0emluagb7uci39ts11cgoh4sk2l0.bitcoin.ninja.".try_into().unwrap(),
994 ty: NSec3::TYPE, alg: 13, labels: 3, orig_ttl: 60, expiration: 1787208167,
995 inception: 1785993167, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
996 signature: base64::decode("sha0PpAzWuFHHXRa2umvNGYuZMy1JaLiDevBJyJmpsyLoskGBA5aLCvhXiQlT913pAPvhXzeXO7JkdPTTpjLag==").unwrap(),
997 };
998 let (txt_resp, txt_rrsig, nsec3_b, nsec3_b_rrsig) = bitcoin_ninja_wildcard_record("asdf");
999 (cname_resp, cname_rrsig, txt_resp, txt_rrsig,
1000 [(nsec3_a, nsec3_a_rrsig), (nsec3_b, nsec3_b_rrsig)])
1001 }
1002
1003 fn bitcoin_ninja_x_domain_cname_wildcard_record() -> (CName, RRSig, NSec3, RRSig) {
1004 let name = "wildcard.x_domain_cname_wild.dnssec_proof_tests.bitcoin.ninja.";
1005 let cname = CName {
1006 name: name.try_into().unwrap(),
1007 canonical_name: "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap(),
1008 };
1009 let cname_rrsig = RRSig {
1010 name: name.try_into().unwrap(), ty: CName::TYPE, alg: 13, labels: 4, orig_ttl: 30,
1011 expiration: 1786663415, inception: 1785448415, key_tag: 62306,
1012 key_name: "bitcoin.ninja.".try_into().unwrap(),
1013 signature: base64::decode("HN8yF2eG5pUMWdUHBXK1PoUSTsCvJ89PrdvRIaTpOfdonxWUB4Mw093TefbGfXAWJYmLRRW4jd0B7LPIc3Jv7A==").unwrap(),
1014 };
1015 let nsec3 = NSec3 {
1016 name: "vpbup1es8cqpj16tgtgtas6fifbu8u73.bitcoin.ninja.".try_into().unwrap(),
1017 hash_algo: 1, flags: 0, hash_iterations: 0,
1018 salt: Vec::from_hex("180E9D1B9F24BAD7").unwrap(),
1019 next_name_hash: Vec::from_hex("02417EDCE26669601E9407D9B0A0E3AB1741B8E8").unwrap(),
1020 types: NSecTypeMask::from_types(&[33, RRSig::TYPE]),
1021 };
1022 let nsec3_rrsig = RRSig {
1023 name: "vpbup1es8cqpj16tgtgtas6fifbu8u73.bitcoin.ninja.".try_into().unwrap(),
1024 ty: NSec3::TYPE, alg: 13, labels: 3, orig_ttl: 60,
1025 expiration: 1786974157, inception: 1785759157, key_tag: 62306,
1026 key_name: "bitcoin.ninja.".try_into().unwrap(),
1027 signature: base64::decode("F3GPW+yCZjHy8VA6BMhlH01ZyDHf94dD1r5bTN+AWDuDw2bPsCtVbrzvS0gNenxKBtOgg9kE3+WYiZkrGhaM5A==").unwrap(),
1028 };
1029 (cname, cname_rrsig, nsec3, nsec3_rrsig)
1030 }
1031
1032 fn bitcoin_ninja_cname_target_wildcard_record() -> (Txt, RRSig, NSec3, RRSig) {
1033 let name = "cname.wildcard_test.dnssec_proof_tests.bitcoin.ninja.";
1034 let txt = Txt {
1035 name: name.try_into().unwrap(),
1036 data: "wildcard_test".try_into().unwrap(),
1037 };
1038 let txt_rrsig = RRSig {
1039 name: name.try_into().unwrap(), ty: Txt::TYPE, alg: 13, labels: 4, orig_ttl: 30,
1040 expiration: 1786663415, inception: 1785448415, key_tag: 62306,
1041 key_name: "bitcoin.ninja.".try_into().unwrap(),
1042 signature: base64::decode("NvmdgkpvkZvnAOZbW9+XwJmV8HqMqreTKsLmrtC0q5GJI/26Z7u8PG3/hjPtEd5wShFfTd1xD3FYywOwkp682Q==").unwrap(),
1043 };
1044 let nsec3 = NSec3 {
1045 name: "k0k9mke3fif9p0oq11707a2oe440utb4.bitcoin.ninja.".try_into().unwrap(),
1046 hash_algo: 1, flags: 0, hash_iterations: 0,
1047 salt: Vec::from_hex("180E9D1B9F24BAD7").unwrap(),
1048 next_name_hash: Vec::from_hex("A62CCD9A3F946729008413B647E0E191E7713009").unwrap(),
1049 types: NSecTypeMask::from_types(&[Txt::TYPE, RRSig::TYPE]),
1050 };
1051 let nsec3_rrsig = RRSig {
1052 name: "k0k9mke3fif9p0oq11707a2oe440utb4.bitcoin.ninja.".try_into().unwrap(),
1053 ty: NSec3::TYPE, alg: 13, labels: 3, orig_ttl: 60, expiration: 1786987366,
1054 inception: 1785772366, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
1055 signature: base64::decode("WhCMAfJ1hZ+QsOXhbbwKpBk9VhsZdfYhjUMXcQHLDolrmj3SETPPAkMiGtm2oBdPXZeWrhpPEmNFnpquEgI3Fg==").unwrap(),
1056 };
1057 (txt, txt_rrsig, nsec3, nsec3_rrsig)
1058 }
1059
1060 fn bitcoin_ninja_override_matching_nsec3(dnskeys: &[&DnsKey]) -> (NSec3, RRSig) {
1064 let overridden = "override.wildcard_test.dnssec_proof_tests.bitcoin.ninja.";
1065 let nsec3 = NSec3 {
1066 name: "i35lj8ddq83urbvibgi143qtdiu5kqlp.bitcoin.ninja.".try_into().unwrap(),
1067 hash_algo: 1, flags: 0, hash_iterations: 0,
1068 salt: Vec::from_hex("180E9D1B9F24BAD7").unwrap(),
1069 next_name_hash: crate::base32::decode("JO06SMGEHDFJ0I3MNHB1HMMNATA3TPIP").unwrap(),
1070 types: NSecTypeMask::from_types(&[Txt::TYPE, RRSig::TYPE]),
1071 };
1072 let nsec3_rrsig = RRSig {
1073 name: "i35lj8ddq83urbvibgi143qtdiu5kqlp.bitcoin.ninja.".try_into().unwrap(),
1074 ty: NSec3::TYPE, alg: 13, labels: 3, orig_ttl: 60,
1075 expiration: 1786987366, inception: 1785772366, key_tag: 62306,
1076 key_name: "bitcoin.ninja.".try_into().unwrap(),
1077 signature: base64::decode("fYjOjpA3JKI1AlVmpmDemxzGxksQ2t3k2ChO0YCNVkp1Z1tvc91pb2SIBC/AZak4Do4qNl7OK9SkFa4otkU4jQ==").unwrap(),
1078 };
1079 verify_rrsig(&nsec3_rrsig, dnskeys.iter().copied(), vec![&nsec3]).unwrap();
1080
1081 let mut hasher = crypto::hash::Hasher::sha1();
1082 write_name(&mut hasher, overridden);
1083 hasher.update(&nsec3.salt);
1084 let (owner_hash_base32, _) = nsec3.name.split_once('.').unwrap();
1085 assert_eq!(&base32::decode(owner_hash_base32).unwrap()[..], hasher.finish().as_ref(),
1086 "NSEC3 no longer matches the name it is supposed to match");
1087 assert!(nsec3.types.contains_type(Txt::TYPE),
1088 "NSEC3 must list TXT as present, else it proves absence by bitmap instead");
1089
1090 (nsec3, nsec3_rrsig)
1091 }
1092
1093 fn bitcoin_ninja_nsec_dnskey() -> (Vec<DnsKey>, Vec<RR>) {
1094 let bitcoin_ninja_dnskeys = bitcoin_ninja_dnskey().0;
1095 let mut bitcoin_ninja_ds = vec![DS {
1096 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1097 key_tag: 8036, alg: 13, digest_type: 2,
1098 digest: Vec::from_hex("8EC0DAE4501233979196EBED206212BCCC49E40E086EC2E56558EC1F6FB62715").unwrap(),
1099 }];
1100 let ds_rrsig = RRSig {
1101 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(), ty: DS::TYPE, alg: 13, labels: 4, orig_ttl: 30,
1102 expiration: 1787178279, inception: 1785963279, key_tag: 62306, key_name: "bitcoin.ninja.".try_into().unwrap(),
1103 signature: base64::decode("a+selfmR4paAGLLAhJVT9x9tkCUDnmzM6yi40A6vKwB8G6vU4VR35gVfMgJjb3weWRAuk8AHoKDf4d0fTiESZA==").unwrap(),
1104 };
1105 verify_rrsig(&ds_rrsig, &bitcoin_ninja_dnskeys, bitcoin_ninja_ds.iter().collect()).unwrap();
1106 let dnskeys = vec![DnsKey {
1107 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
1108 pubkey: base64::decode("cpJjguqPE/pALvfrer/FgTsU+Z/lqlzP0jR2uH1GJZ3XhScsAP2YLWwL+J+v0TQJNBBLxLIchjpxe7pYo0l16w==").unwrap(),
1109 }, DnsKey {
1110 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(), flags: 256, protocol: 3, alg: 13,
1111 pubkey: base64::decode("j4xLO1IMaoL6fNuB8lssMVTg4CvK8GZpyf5KVCSjmSueXJPMrpAvDvMpEgcrpi5nZr0CR132Sml9BT5E1cPVPg==").unwrap(),
1112 }, DnsKey {
1113 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(), flags: 257, protocol: 3, alg: 13,
1114 pubkey: base64::decode("MUnIhm31ySIr9WXIBVQc38wlSHHvYaKIOFR8WYl4O9MJBlywWeUdx16oGinCe2FjjMkUkKn9kV5zzWhGmrdIbQ==").unwrap(),
1115 }];
1116 let dnskey_rrsig = RRSig {
1117 name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1118 ty: DnsKey::TYPE, alg: 13, labels: 4, orig_ttl: 604800, expiration: 1786993013,
1119 inception: 1785778013, key_tag: 8036, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1120 signature: base64::decode("o+ve+8i6LwCpqZN5yS9CVsJOVIkjR5baM8Nf6m6HLp5Zfjkbi2ux3PjmwhKe7k1SitH29nK59xc5+8St4pO4iw==").unwrap(),
1121 };
1122 verify_dnskeys([&dnskey_rrsig], &bitcoin_ninja_ds, dnskeys.iter().collect()).unwrap();
1123 let rrs = vec![bitcoin_ninja_ds.pop().unwrap().into(), ds_rrsig.into(),
1124 dnskeys[0].clone().into(), dnskeys[1].clone().into(), dnskeys[2].clone().into(),
1125 dnskey_rrsig.into()];
1126 (dnskeys, rrs)
1127 }
1128
1129 fn bitcoin_ninja_nsec_record() -> (Txt, RRSig) {
1130 let txt_resp = Txt {
1131 name: "a.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1132 data: "txt_a".try_into().unwrap(),
1133 };
1134 let txt_rrsig = RRSig {
1135 name: "a.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1136 ty: Txt::TYPE, alg: 13, labels: 5, orig_ttl: 30, expiration: 1787000213,
1137 inception: 1785785213, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1138 signature: base64::decode("+H03kpGz5pum4HmL1EgQXq8EF1QSbWsoiWA5NFWlX+bhd3mnQFZ8WyZzvkFI/T6KPhBsoEwAECh6FWtACLFkKA==").unwrap(),
1139 };
1140 (txt_resp, txt_rrsig)
1141 }
1142
1143 fn bitcoin_ninja_nsec_wildcard_record(pfx: &str) -> (Txt, RRSig, NSec, RRSig) {
1144 let name: Name = (pfx.to_owned() + ".wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
1145 let txt_resp = Txt {
1146 name: name.clone(),
1147 data: "wildcard_test".try_into().unwrap(),
1148 };
1149 let txt_rrsig = RRSig {
1150 name: name.clone(),
1151 ty: Txt::TYPE, alg: 13, labels: 5, orig_ttl: 30, expiration: 1787000213,
1152 inception: 1785785213, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1153 signature: base64::decode("5+BP2xI1BraLWhRS6RED4Ji8SlbK0rO0OpnrDohCpwQpd8HTPzhznOXZwfptN+GGruHJ1jHDmPsvWINf9n5tUg==").unwrap(),
1154 };
1155 let nsec = NSec {
1156 name: "*.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1157 next_name: "override.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1158 types: NSecTypeMask::from_types(&[Txt::TYPE, RRSig::TYPE, NSec::TYPE]),
1159 };
1160 let nsec_rrsig = RRSig {
1161 name: "*.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1162 ty: NSec::TYPE, alg: 13, labels: 5, orig_ttl: 60, expiration: 1787232803,
1163 inception: 1786017803, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1164 signature: base64::decode("eDqJ3Ly6uVa1phZPle8wChh5AYuy67TRIkeccJRUOv6uZlKcU+2CZ6UcesMjgXnjBbIlXuw0B0/hN78KT61LGQ==").unwrap(),
1165 };
1166 (txt_resp, txt_rrsig, nsec, nsec_rrsig)
1167 }
1168
1169 fn bitcoin_ninja_nsec_post_override_wildcard_record(pfx: &str) -> (Txt, RRSig, NSec, RRSig) {
1170 let name: Name = (pfx.to_owned() + ".wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
1171 let txt_resp = Txt {
1172 name: name.clone(),
1173 data: "wildcard_test".try_into().unwrap(),
1174 };
1175 let txt_rrsig = RRSig {
1176 name: name.clone(),
1177 ty: Txt::TYPE, alg: 13, labels: 5, orig_ttl: 30, expiration: 1787000213,
1178 inception: 1785785213, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1179 signature: base64::decode("5+BP2xI1BraLWhRS6RED4Ji8SlbK0rO0OpnrDohCpwQpd8HTPzhznOXZwfptN+GGruHJ1jHDmPsvWINf9n5tUg==").unwrap(),
1180 };
1181 let nsec = NSec {
1182 name: "override.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1183 next_name: "nested.zent_test.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1184 types: NSecTypeMask::from_types(&[16, 46, 47]),
1185 };
1186 let nsec_rrsig = RRSig {
1187 name: "override.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1188 ty: NSec::TYPE, alg: 13, labels: 6, orig_ttl: 60, expiration: 1787232803,
1189 inception: 1786017803, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1190 signature: base64::decode("FFmyd7Bmq8hmFmZOyNMfvJkCpdU9po4ogA41xUw+nZNLvtad/CN/7p9nJTcHTvmp0UEyg5t2K9yV9KhNRlaASg==").unwrap(),
1191 };
1192 (txt_resp, txt_rrsig, nsec, nsec_rrsig)
1193 }
1194
1195 fn bitcoin_ninja_nsec_wraparound_wildcard_record(pfx: &str) -> (Txt, RRSig, NSec, RRSig) {
1196 let name: Name = (pfx.to_owned() + ".wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
1197 let txt_resp = Txt {
1198 name: name.clone(),
1199 data: "wildcard_test".try_into().unwrap(),
1200 };
1201 let txt_rrsig = RRSig {
1202 name: name.clone(),
1203 ty: Txt::TYPE, alg: 13, labels: 5, orig_ttl: 30, expiration: 1787000213,
1204 inception: 1785785213, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1205 signature: base64::decode("5+BP2xI1BraLWhRS6RED4Ji8SlbK0rO0OpnrDohCpwQpd8HTPzhznOXZwfptN+GGruHJ1jHDmPsvWINf9n5tUg==").unwrap(),
1206 };
1207 let nsec = NSec {
1208 name: "nested.zent_test.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1209 next_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1210 types: NSecTypeMask::from_types(&[Txt::TYPE, RRSig::TYPE, NSec::TYPE]),
1211 };
1212 let nsec_rrsig = RRSig {
1213 name: "nested.zent_test.wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1214 ty: NSec::TYPE, alg: 13, labels: 7, orig_ttl: 60, expiration: 1787232803,
1215 inception: 1786017803, key_tag: 37278, key_name: "nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap(),
1216 signature: base64::decode("y11K2pyk6NVCbXtdI4fFt8J9ox1kFUnpjMn8kocY3HYuU1eNdpPvOkEmVRmXyULDvgsksRdJ4yYqmrkeMQx9Lw==").unwrap(),
1217 };
1218 (txt_resp, txt_rrsig, nsec, nsec_rrsig)
1219 }
1220
1221 #[test]
1222 fn check_txt_record_a() {
1223 let dnskeys = mattcorallo_dnskey().0;
1224 let (txts, txt_rrsig) = mattcorallo_txt_record();
1225 verify_rrsig(&txt_rrsig, &dnskeys, txts.iter().collect()).unwrap();
1226 }
1227
1228 #[test]
1229 fn check_revoked_dnskey_is_not_used() {
1230 let dnskeys = bitcoin_ninja_dnskey().0;
1231 let (txt, txt_rrsig) = bitcoin_ninja_txt_record();
1232 let txt_resp = [txt];
1233 verify_rrsig(&txt_rrsig, &dnskeys, txt_resp.iter().collect()).unwrap();
1234
1235 let signing_key = dnskeys.iter().find(|key| key.key_tag() == txt_rrsig.key_tag).unwrap();
1236
1237 let mut revoked = signing_key.clone();
1241 revoked.flags |= 0b0_1000_0000;
1242 let fixup_idx = revoked.pubkey.iter().enumerate()
1243 .position(|(idx, byte)| idx % 2 == 1 && *byte >= 0x80).unwrap();
1244 revoked.pubkey[fixup_idx] -= 0x80;
1245 assert_eq!(revoked.key_tag(), signing_key.key_tag());
1246 assert_ne!(revoked.pubkey, signing_key.pubkey);
1247
1248 verify_rrsig(&txt_rrsig, vec![&revoked, signing_key], txt_resp.iter().collect()).unwrap();
1252 }
1253
1254 #[test]
1255 fn check_single_txt_proof() {
1256 let mut rr_stream = Vec::new();
1257 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1258 for rr in com_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1259 for rr in mattcorallo_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1260 let (txts, txt_rrsig) = mattcorallo_txt_record();
1261 for txt in txts { write_rr(&RR::Txt(txt), 1, &mut rr_stream); }
1262 write_rr(&RR::RRSig(txt_rrsig), 1, &mut rr_stream);
1263
1264 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1265 rrs.shuffle(&mut rand::rngs::OsRng);
1266 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1267 let mut txts = verified_rrs.verified_rrs.iter()
1268 .map(|rr| if let RR::Txt(txt) = rr { txt } else { panic!() })
1269 .collect::<Vec<_>>();
1270 txts.sort();
1271 assert_eq!(txts.len(), 2);
1272 assert!(txts.iter().all(|txt| txt.name.as_str() == "matt.user._bitcoin-payment.mattcorallo.com."));
1273 assert_eq!(txts[0].data.as_vec(),
1274 b"as long as it doesn't start with bitcoin:, other records should be ignored");
1275 assert_eq!(txts[1].data.as_vec(),
1276 b"bitcoin:bc1qztwy6xen3zdtt7z0vrgapmjtfz8acjkfp5fp7l?lno=lno1zr5qyugqgskrk70kqmuq7v3dnr2fnmhukps9n8hut48vkqpqnskt2svsqwjakp7k6pyhtkuxw7y2kqmsxlwruhzqv0zsnhh9q3t9xhx39suc6qsr07ekm5esdyum0w66mnx8vdquwvp7dp5jp7j3v5cp6aj0w329fnkqqv60q96sz5nkrc5r95qffx002q53tqdk8x9m2tmt85jtpmcycvfnrpx3lr45h2g7na3sec7xguctfzzcm8jjqtj5ya27te60j03vpt0vq9tm2n9yxl2hngfnmygesa25s4u4zlxewqpvp94xt7rur4rhxunwkthk9vly3lm5hh0pqv4aymcqejlgssnlpzwlggykkajp7yjs5jvr2agkyypcdlj280cy46jpynsezrcj2kwa2lyr8xvd6lfkph4xrxtk2xc3lpq");
1277 assert_eq!(verified_rrs.valid_from, 1785988800); assert_eq!(verified_rrs.expires, 1786415920); assert_eq!(verified_rrs.max_cache_ttl, 3600); }
1281
1282 #[test]
1283 fn check_txt_record_b() {
1284 let dnskeys = bitcoin_ninja_dnskey().0;
1285 let (txt, txt_rrsig) = bitcoin_ninja_txt_record();
1286 let txt_resp = [txt];
1287 verify_rrsig(&txt_rrsig, &dnskeys, txt_resp.iter().collect()).unwrap();
1288 }
1289
1290 #[test]
1291 fn check_cname_record() {
1292 let dnskeys = bitcoin_ninja_dnskey().0;
1293 let (cname, cname_rrsig) = bitcoin_ninja_cname_record();
1294 let cname_resp = [cname];
1295 verify_rrsig(&cname_rrsig, &dnskeys, cname_resp.iter().collect()).unwrap();
1296 }
1297
1298 #[test]
1299 fn check_dname_does_not_capture_sibling_name() {
1300 let resolve = |extra: Vec<RR>| -> Vec<RR> {
1303 let mut rr_stream = Vec::new();
1304 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1305 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1306 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1307 let (dname, dname_rrsig) = bitcoin_ninja_dname_record();
1308 for rr in [RR::DName(dname), RR::RRSig(dname_rrsig)] {
1309 write_rr(&rr, 1, &mut rr_stream);
1310 }
1311 for rr in extra { write_rr(&rr, 1, &mut rr_stream); }
1312
1313 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1314 rrs.shuffle(&mut rand::rngs::OsRng);
1315 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1316 let name = "notdname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
1317 verified_rrs.resolve_name(&name).into_iter().cloned().collect()
1318 };
1319
1320 let (sibling, sibling_rrsig, redirected, redirected_rrsig) = bitcoin_ninja_dname_sibling_records();
1321
1322 assert_eq!(resolve(Vec::new()), Vec::new());
1323 assert_eq!(resolve(vec![RR::Txt(redirected.clone()), RR::RRSig(redirected_rrsig.clone())]), Vec::new());
1324
1325 let resolved = resolve(vec![RR::Txt(sibling.clone()), RR::RRSig(sibling_rrsig.clone())]);
1326 let resolved_full = resolve(vec![
1327 RR::Txt(sibling), RR::RRSig(sibling_rrsig), RR::Txt(redirected), RR::RRSig(redirected_rrsig),
1328 ]);
1329 assert_eq!(resolved, resolved_full);
1330 assert_eq!(resolved.len(), 1);
1331 if let RR::Txt(txt) = &resolved[0] {
1332 assert_eq!(txt.name.as_str(), "notdname_test.dnssec_proof_tests.bitcoin.ninja.");
1333 assert_eq!(txt.data.as_vec(), b"not_dnamed");
1334 } else { panic!(); }
1335 }
1336
1337 #[test]
1338 fn check_dname_label_boundaries() {
1339 let cases: &[(&str, &str, &str, Option<&str>)] = &[
1342 ("a.example.com.", "example.com.", "example.net.", Some("a.example.net.")),
1344 ("a.b.example.com.", "example.com.", "example.net.", Some("a.b.example.net.")),
1345 ("a.x.example.com.", "x.example.com.", "example.net.", Some("a.example.net.")),
1346 ("a.example.com.", "example.com.", "y.example.net.", Some("a.y.example.net.")),
1347 ("ab.example.com.", "b.example.com.", "example.net.", None),
1349 ("example.com.", "example.com.", "example.net.", None),
1351 ("a.x.", "x.", ".", Some("a.")),
1353 ];
1354 for (query, owner, target, expected) in cases {
1355 let landing = expected.unwrap_or(query);
1357 let rrs = vec![
1358 RR::DName(DName {
1359 name: (*owner).try_into().unwrap(),
1360 delegation_name: (*target).try_into().unwrap(),
1361 }),
1362 RR::Txt(Txt {
1363 name: landing.try_into().unwrap(),
1364 data: (*landing).try_into().unwrap(),
1365 }),
1366 ];
1367 let stream = VerifiedRRStream {
1368 verified_rrs: rrs.iter().collect(),
1369 valid_from: 0, expires: u64::MAX, max_cache_ttl: 0,
1370 };
1371 let resolved = stream.resolve_name(&(*query).try_into().unwrap());
1374 let txts: Vec<_> = resolved.iter()
1375 .filter_map(|rr| if let RR::Txt(txt) = rr { Some(txt) } else { None }).collect();
1376 assert_eq!(txts.len(), 1, "{} + DNAME {} -> {}", query, owner, target);
1377 assert_eq!(txts[0].name.as_str(), landing, "{} + DNAME {} -> {}", query, owner, target);
1378 }
1379 }
1380
1381 #[test]
1382 fn check_alias_loops_terminate() {
1383 let cname_cycle = vec![
1386 RR::CName(CName {
1387 name: "a.example.com.".try_into().unwrap(),
1388 canonical_name: "b.example.com.".try_into().unwrap(),
1389 }),
1390 RR::CName(CName {
1391 name: "b.example.com.".try_into().unwrap(),
1392 canonical_name: "a.example.com.".try_into().unwrap(),
1393 }),
1394 ];
1395 let self_dname = vec![
1396 RR::DName(DName {
1397 name: "example.com.".try_into().unwrap(),
1398 delegation_name: "example.com.".try_into().unwrap(),
1399 }),
1400 ];
1401 for (rrs, query) in [(cname_cycle, "a.example.com."), (self_dname, "cyc.example.com.")] {
1402 let stream = VerifiedRRStream {
1403 verified_rrs: rrs.iter().collect(),
1404 valid_from: 0, expires: u64::MAX, max_cache_ttl: 0,
1405 };
1406 assert!(stream.resolve_name(&query.try_into().unwrap()).is_empty());
1407 }
1408 }
1409
1410 #[test]
1411 fn check_multi_zone_proof() {
1412 let mut rr_stream = Vec::new();
1413 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1414 for rr in com_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1415 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1416 for rr in mattcorallo_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1417 let (txts, txt_rrsig) = mattcorallo_txt_record();
1418 for txt in txts { write_rr(&RR::Txt(txt), 1, &mut rr_stream); }
1419 write_rr(&RR::RRSig(txt_rrsig), 1, &mut rr_stream);
1420 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1421 let (txt, txt_rrsig) = bitcoin_ninja_txt_record();
1422 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1423 let (cname, cname_rrsig) = bitcoin_ninja_cname_record();
1424 for rr in [RR::CName(cname), RR::RRSig(cname_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1425
1426 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1427 rrs.shuffle(&mut rand::rngs::OsRng);
1428 let mut verified_rrs = verify_rr_stream(&rrs).unwrap();
1429 verified_rrs.verified_rrs.sort();
1430 assert_eq!(verified_rrs.verified_rrs.len(), 4);
1431 if let RR::Txt(txt) = &verified_rrs.verified_rrs[0] {
1432 assert_eq!(txt.name.as_str(), "matt.user._bitcoin-payment.mattcorallo.com.");
1433 assert_eq!(txt.data.as_vec(),
1434 b"as long as it doesn't start with bitcoin:, other records should be ignored");
1435 } else { panic!(); }
1436 if let RR::Txt(txt) = &verified_rrs.verified_rrs[1] {
1437 assert_eq!(txt.name.as_str(), "matt.user._bitcoin-payment.mattcorallo.com.");
1438 assert_eq!(txt.data.as_vec(),
1439 b"bitcoin:bc1qztwy6xen3zdtt7z0vrgapmjtfz8acjkfp5fp7l?lno=lno1zr5qyugqgskrk70kqmuq7v3dnr2fnmhukps9n8hut48vkqpqnskt2svsqwjakp7k6pyhtkuxw7y2kqmsxlwruhzqv0zsnhh9q3t9xhx39suc6qsr07ekm5esdyum0w66mnx8vdquwvp7dp5jp7j3v5cp6aj0w329fnkqqv60q96sz5nkrc5r95qffx002q53tqdk8x9m2tmt85jtpmcycvfnrpx3lr45h2g7na3sec7xguctfzzcm8jjqtj5ya27te60j03vpt0vq9tm2n9yxl2hngfnmygesa25s4u4zlxewqpvp94xt7rur4rhxunwkthk9vly3lm5hh0pqv4aymcqejlgssnlpzwlggykkajp7yjs5jvr2agkyypcdlj280cy46jpynsezrcj2kwa2lyr8xvd6lfkph4xrxtk2xc3lpq");
1440 } else { panic!(); }
1441 if let RR::Txt(txt) = &verified_rrs.verified_rrs[2] {
1442 assert_eq!(txt.name.as_str(), "txt_test.dnssec_proof_tests.bitcoin.ninja.");
1443 assert_eq!(txt.data.as_vec(), b"dnssec_prover_test");
1444 } else { panic!(); }
1445 if let RR::CName(cname) = &verified_rrs.verified_rrs[3] {
1446 assert_eq!(cname.name.as_str(), "cname_test.dnssec_proof_tests.bitcoin.ninja.");
1447 assert_eq!(cname.canonical_name.as_str(), "txt_test.dnssec_proof_tests.bitcoin.ninja.");
1448 } else { panic!(); }
1449
1450 let filtered_rrs =
1451 verified_rrs.resolve_name(&"cname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap());
1452 assert_eq!(filtered_rrs.len(), 1);
1453 if let RR::Txt(txt) = &filtered_rrs[0] {
1454 assert_eq!(txt.name.as_str(), "txt_test.dnssec_proof_tests.bitcoin.ninja.");
1455 assert_eq!(txt.data.as_vec(), b"dnssec_prover_test");
1456 } else { panic!(); }
1457 }
1458
1459 #[test]
1460 fn check_wildcard_record() {
1461 let dnskeys = bitcoin_ninja_dnskey().0;
1463 let (txt, txt_rrsig, _, _) = bitcoin_ninja_wildcard_record("name");
1464 let txt_resp = [txt];
1465 verify_rrsig(&txt_rrsig, &dnskeys, txt_resp.iter().collect()).unwrap();
1466
1467 let (txt, txt_rrsig, _, _) = bitcoin_ninja_wildcard_record("anoter_name");
1468 let txt_resp = [txt];
1469 verify_rrsig(&txt_rrsig, &dnskeys, txt_resp.iter().collect()).unwrap();
1470
1471 let (txt, txt_rrsig, _, _) = bitcoin_ninja_wildcard_record("multiple.names");
1472 let txt_resp = [txt];
1473 verify_rrsig(&txt_rrsig, &dnskeys, txt_resp.iter().collect()).unwrap();
1474 }
1475
1476 #[test]
1477 fn check_wildcard_proof() {
1478 let mut rr_stream = Vec::new();
1479 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1480 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1481 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1482 let (cname, cname_rrsig, txt, txt_rrsig, nsec3s) = bitcoin_ninja_cname_wildcard_record();
1483 for rr in [RR::CName(cname), RR::RRSig(cname_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1484 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1485 for (rra, rrb) in nsec3s { write_rr(&rra, 1, &mut rr_stream); write_rr(&rrb, 1, &mut rr_stream); }
1486
1487 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1488 rrs.shuffle(&mut rand::rngs::OsRng);
1489 let mut verified_rrs = verify_rr_stream(&rrs).unwrap();
1490 verified_rrs.verified_rrs.sort();
1491 assert_eq!(verified_rrs.verified_rrs.len(), 2);
1492 if let RR::Txt(txt) = &verified_rrs.verified_rrs[0] {
1493 assert_eq!(txt.name.as_str(), "asdf.wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
1494 assert_eq!(txt.data.as_vec(), b"wildcard_test");
1495 } else { panic!(); }
1496 if let RR::CName(cname) = &verified_rrs.verified_rrs[1] {
1497 assert_eq!(cname.name.as_str(), "asdf.cname_wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
1498 assert_eq!(cname.canonical_name.as_str(), "cname.wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
1499 } else { panic!(); }
1500
1501 let filtered_rrs =
1502 verified_rrs.resolve_name(&"asdf.wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap());
1503 assert_eq!(filtered_rrs.len(), 1);
1504 if let RR::Txt(txt) = &filtered_rrs[0] {
1505 assert_eq!(txt.name.as_str(), "asdf.wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
1506 assert_eq!(txt.data.as_vec(), b"wildcard_test");
1507 } else { panic!(); }
1508 }
1509
1510 #[test]
1511 fn check_simple_nsec_zone_proof() {
1512 let mut rr_stream = Vec::new();
1513 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1514 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1515 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1516 for rr in bitcoin_ninja_nsec_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1517 let (txt, txt_rrsig) = bitcoin_ninja_nsec_record();
1518 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1519
1520 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1521 rrs.shuffle(&mut rand::rngs::OsRng);
1522 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1523 let filtered_rrs =
1524 verified_rrs.resolve_name(&"a.nsec_tests.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap());
1525 assert_eq!(filtered_rrs.len(), 1);
1526 if let RR::Txt(txt) = &filtered_rrs[0] {
1527 assert_eq!(txt.name.as_str(), "a.nsec_tests.dnssec_proof_tests.bitcoin.ninja.");
1528 assert_eq!(txt.data.as_vec(), b"txt_a");
1529 } else { panic!(); }
1530 }
1531
1532 #[test]
1533 fn check_nsec_wildcard_proof() {
1534 let check_proof = |pfx: &str, post_override: bool| -> Result<(), ()> {
1535 let mut rr_stream = Vec::new();
1536 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1537 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1538 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1539 for rr in bitcoin_ninja_nsec_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1540 let (txt, txt_rrsig, nsec, nsec_rrsig) = if post_override {
1541 bitcoin_ninja_nsec_post_override_wildcard_record(pfx)
1542 } else {
1543 bitcoin_ninja_nsec_wildcard_record(pfx)
1544 };
1545 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1546 for rr in [RR::NSec(nsec), RR::RRSig(nsec_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1547
1548 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1549 rrs.shuffle(&mut rand::rngs::OsRng);
1550 let verified_rrs = verify_rr_stream(&rrs).map_err(|_| ())?;
1553 let name: Name =
1554 (pfx.to_owned() + ".wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
1555 let filtered_rrs = verified_rrs.resolve_name(&name);
1556 assert_eq!(filtered_rrs.len(), 1);
1557 if let RR::Txt(txt) = &filtered_rrs[0] {
1558 assert_eq!(txt.name, name);
1559 assert_eq!(txt.data.as_vec(), b"wildcard_test");
1560 } else { panic!(); }
1561 Ok(())
1562 };
1563 check_proof("a", false).unwrap();
1566 check_proof("a", true).unwrap_err();
1567 check_proof("a.b", false).unwrap();
1568 check_proof("a.b", true).unwrap_err();
1569 check_proof("o", false).unwrap();
1570 check_proof("o", true).unwrap_err();
1571 check_proof("a.o", false).unwrap();
1572 check_proof("a.o", true).unwrap_err();
1573 check_proof("override", false).unwrap_err();
1574 check_proof("override", true).unwrap_err();
1575 check_proof("b.override", false).unwrap_err();
1578 check_proof("b.override", true).unwrap_err();
1579 check_proof("z", false).unwrap_err();
1580 check_proof("z", true).unwrap();
1581 check_proof("a.z", false).unwrap_err();
1582 check_proof("a.z", true).unwrap();
1583 }
1584
1585 #[test]
1586 fn check_online_signed_nsec_next_name() {
1587 let dnskeys = cloudflare_dnskey().0;
1588 let (nsec, nsec_rrsig) = cloudflare_online_signed_nsec();
1589 assert_eq!(&nsec.next_name[..], b"\x00.online_signing_test.cloudflare.com.");
1590
1591 let mut rr_stream = Vec::new();
1593 write_rr(&nsec, 300, &mut rr_stream);
1594 assert_eq!(parse_rr_stream(&rr_stream).unwrap(), vec![RR::NSec(nsec.clone())]);
1595
1596 verify_rrsig(&nsec_rrsig, &dnskeys, vec![&nsec]).unwrap();
1597
1598 assert_eq!(StaticRecord::json(&nsec), "{\"type\":\"nsec\",\
1599 \"name\":\"online_signing_test.cloudflare.com.\",\
1600 \"next_name\":\"\\u0000.online_signing_test.cloudflare.com.\",\
1601 \"types\":[\"NSEC\",\"RRSIG\",128]}");
1602 }
1603
1604 #[test]
1605 fn check_nsec_wraparound_wildcard_proof() {
1606 let check_proof = |pfx: &str| -> Result<(), ()> {
1607 let mut rr_stream = Vec::new();
1608 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1609 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1610 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1611 for rr in bitcoin_ninja_nsec_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1612 let (txt, txt_rrsig, nsec, nsec_rrsig) =
1613 bitcoin_ninja_nsec_wraparound_wildcard_record(pfx);
1614 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1615 for rr in [RR::NSec(nsec), RR::RRSig(nsec_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1616
1617 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1618 rrs.shuffle(&mut rand::rngs::OsRng);
1619 let verified_rrs = verify_rr_stream(&rrs).map_err(|_| ())?;
1620 let name: Name =
1621 (pfx.to_owned() + ".wildcard_test.nsec_tests.dnssec_proof_tests.bitcoin.ninja.").try_into().unwrap();
1622 let filtered_rrs = verified_rrs.resolve_name(&name);
1623 assert_eq!(filtered_rrs.len(), 1);
1624 if let RR::Txt(txt) = &filtered_rrs[0] {
1625 assert_eq!(txt.name, name);
1626 assert_eq!(txt.data.as_vec(), b"wildcard_test");
1627 } else { panic!(); }
1628 Ok(())
1629 };
1630
1631 check_proof("zz").unwrap();
1633 check_proof("a.zz").unwrap();
1634 check_proof("a").unwrap_err();
1636 check_proof("nested.zent_test").unwrap_err();
1637 }
1638
1639 #[test]
1640 fn check_descendant_zone_denial_rejected() {
1641 let build = |proof: Vec<RR>| {
1644 let mut rr_stream = Vec::new();
1645 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1646 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1647 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1648 for rr in bitcoin_ninja_nsec_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1649 let (txt, txt_rrsig, _, _) = bitcoin_ninja_wildcard_record("asdf");
1650 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1651 for rr in proof { write_rr(&rr, 1, &mut rr_stream); }
1652 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1653 rrs.shuffle(&mut rand::rngs::OsRng);
1654 verify_rr_stream(&rrs).map(|_| ())
1655 };
1656
1657 let (_, _, nsec3, nsec3_rrsig) = bitcoin_ninja_wildcard_record("asdf");
1659 build(vec![RR::NSec3(nsec3), RR::RRSig(nsec3_rrsig)]).unwrap();
1660
1661 let (_, _, nsec, nsec_rrsig) = bitcoin_ninja_nsec_wraparound_wildcard_record("a");
1668 assert_eq!(build(vec![RR::NSec(nsec), RR::RRSig(nsec_rrsig)]).unwrap_err(),
1669 ValidationError::Invalid);
1670 }
1671
1672 #[test]
1673 fn check_nsec_empty_non_terminal_is_not_nonexistence() {
1674 let build = |pfx: &str| {
1687 let mut rr_stream = Vec::new();
1688 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1689 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1690 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1691 for rr in bitcoin_ninja_nsec_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1692 let (txt, txt_rrsig, nsec, nsec_rrsig) =
1693 bitcoin_ninja_nsec_post_override_wildcard_record(pfx);
1694 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1695 for rr in [RR::NSec(nsec), RR::RRSig(nsec_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1696 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1697 rrs.shuffle(&mut rand::rngs::OsRng);
1698 verify_rr_stream(&rrs).map(|_| ())
1699 };
1700
1701 build("z").unwrap();
1705
1706 assert_eq!(build("x.zent_test").unwrap_err(), ValidationError::Invalid);
1710 assert_eq!(build("y.x.zent_test").unwrap_err(), ValidationError::Invalid);
1712 }
1713
1714
1715 #[test]
1716 fn check_txt_sort_order() {
1717 let mut rr_stream = Vec::new();
1718 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1719 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1720 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1721 let (mut txts, rrsig) = bitcoin_ninja_txt_sort_edge_cases_records();
1722 write_rr(&rrsig, 1, &mut rr_stream);
1723 for txt in txts.iter() { write_rr(txt, 1, &mut rr_stream); }
1724
1725 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1726 rrs.shuffle(&mut rand::rngs::OsRng);
1727 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1728 let mut verified_txts = verified_rrs.verified_rrs
1729 .iter().map(|rr| if let RR::Txt(txt) = rr { txt.clone() } else { panic!(); })
1730 .collect::<Vec<_>>();
1731 verified_txts.sort();
1732 txts.sort();
1733 assert_eq!(verified_txts, txts);
1734 }
1735
1736 #[test]
1737 fn rfc9102_parse_test() {
1738 let rfc9102_test_vector = Vec::from_hex("045f343433045f74637003777777076578616d706c6503636f6d000034000100000e1000230301018bd1da95272f7fa4ffb24137fc0ed03aae67e5c4d8b3c50734e1050a7920b922045f343433045f74637003777777076578616d706c6503636f6d00002e000100000e10005f00340d0500000e105fc6d9005bfdda80074e076578616d706c6503636f6d00ce1d3adeb7dc7cee656d61cfb472c5977c8c9caeae9b765155c518fb107b6a1fe0355fbaaf753c192832fa621fa73a8b85ed79d374117387598fcc812e1ef3fb076578616d706c6503636f6d000030000100000e1000440101030d2670355e0c894d9cfea6c5af6eb7d458b57a50ba88272512d8241d8541fd54adf96ec956789a51ceb971094b3bb3f4ec49f64c686595be5b2e89e8799c7717cc076578616d706c6503636f6d00002e000100000e10005f00300d0200000e105fc6d9005bfdda80074e076578616d706c6503636f6d004628383075b8e34b743a209b27ae148d110d4e1a246138a91083249cb4a12a2d9bc4c2d7ab5eb3afb9f5d1037e4d5da8339c162a9298e9be180741a8ca74accc076578616d706c6503636f6d00002b00010002a3000024074e0d02e9b533a049798e900b5c29c90cd25a986e8a44f319ac3cd302bafc08f5b81e16076578616d706c6503636f6d00002e00010002a3000057002b0d020002a3005fc6d9005bfdda80861703636f6d00a203e704a6facbeb13fc9384fdd6de6b50de5659271f38ce81498684e6363172d47e2319fdb4a22a58a231edc2f1ff4fb2811a1807be72cb5241aa26fdaee03903636f6d00003000010002a30000440100030dec8204e43a25f2348c52a1d3bce3a265aa5d11b43dc2a471162ff341c49db9f50a2e1a41caf2e9cd20104ea0968f7511219f0bdc56b68012cc3995336751900b03636f6d00003000010002a30000440101030d45b91c3bef7a5d99a7a7c8d822e33896bc80a777a04234a605a4a8880ec7efa4e6d112c73cd3d4c65564fa74347c873723cc5f643370f166b43dedff836400ff03636f6d00003000010002a30000440101030db3373b6e22e8e49e0e1e591a9f5bd9ac5e1a0f86187fe34703f180a9d36c958f71c4af48ce0ebc5c792a724e11b43895937ee53404268129476eb1aed323939003636f6d00002e00010002a300005700300d010002a3005fc6d9005bfdda8049f303636f6d0018a948eb23d44f80abc99238fcb43c5a18debe57004f7343593f6deb6ed71e04654a433f7aa1972130d9bd921c73dcf63fcf665f2f05a0aaebafb059dc12c96503636f6d00002e00010002a300005700300d010002a3005fc6d9005bfdda80708903636f6d006170e6959bd9ed6e575837b6f580bd99dbd24a44682b0a359626a246b1812f5f9096b75e157e77848f068ae0085e1a609fc19298c33b736863fbccd4d81f5eb203636f6d00002b000100015180002449f30d0220f7a9db42d0e2042fbbb9f9ea015941202f9eabb94487e658c188e7bcb5211503636f6d00002b000100015180002470890d02ad66b3276f796223aa45eda773e92c6d98e70643bbde681db342a9e5cf2bb38003636f6d00002e0001000151800053002b0d01000151805fc6d9005bfdda807cae00122e276d45d9e9816f7922ad6ea2e73e82d26fce0a4b718625f314531ac92f8ae82418df9b898f989d32e80bc4deaba7c4a7c8f172adb57ced7fb5e77a784b0700003000010001518000440100030dccacfe0c25a4340fefba17a254f706aac1f8d14f38299025acc448ca8ce3f561f37fc3ec169fe847c8fcbe68e358ff7c71bb5ee1df0dbe518bc736d4ce8dfe1400003000010001518000440100030df303196789731ddc8a6787eff24cacfeddd032582f11a75bb1bcaa5ab321c1d7525c2658191aec01b3e98ab7915b16d571dd55b4eae51417110cc4cdd11d171100003000010001518000440101030dcaf5fe54d4d48f16621afb6bd3ad2155bacf57d1faad5bac42d17d948c421736d9389c4c4011666ea95cf17725bd0fa00ce5e714e4ec82cfdfacc9b1c863ad4600002e000100015180005300300d00000151805fc6d9005bfdda80b79d00de7a6740eeecba4bda1e5c2dd4899b2c965893f3786ce747f41e50d9de8c0a72df82560dfb48d714de3283ae99a49c0fcb50d3aaadb1a3fc62ee3a8a0988b6be").unwrap();
1741
1742 let mut rrs = parse_rr_stream(&rfc9102_test_vector).unwrap();
1743 rrs.shuffle(&mut rand::rngs::OsRng);
1744 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1745 assert_eq!(verified_rrs.verified_rrs.len(), 1);
1746 if let RR::TLSA(tlsa) = &verified_rrs.verified_rrs[0] {
1747 assert_eq!(tlsa.cert_usage, 3);
1748 assert_eq!(tlsa.selector, 1);
1749 assert_eq!(tlsa.data_ty, 1);
1750 assert_eq!(tlsa.data, Vec::from_hex("8bd1da95272f7fa4ffb24137fc0ed03aae67e5c4d8b3c50734e1050a7920b922").unwrap());
1751 } else { panic!(); }
1752 }
1753
1754 #[test]
1755 fn check_nsec3_wraparound_wildcard_proof() {
1756 let mut rr_stream = Vec::new();
1761 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1762 for rr in com_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1763 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1764 for rr in mattcorallo_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1765 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1766 let (cname, cname_rrsig, nsec3, nsec3_rrsig) = bitcoin_ninja_x_domain_cname_wildcard_record();
1767 for rr in [RR::CName(cname), RR::RRSig(cname_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1768 for rr in [RR::NSec3(nsec3), RR::RRSig(nsec3_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1769 let (txts, txt_rrsig) = mattcorallo_txt_record();
1770 for txt in txts { write_rr(&txt, 1, &mut rr_stream); }
1771 write_rr(&txt_rrsig, 1, &mut rr_stream);
1772
1773 let mut rrs = parse_rr_stream(&rr_stream).unwrap();
1774
1775 let mut wrapping_nsec3s = 0;
1777 for nsec3 in rrs.iter().filter_map(|rr| if let RR::NSec3(n) = rr { Some(n) } else { None }) {
1778 let (start_hash_base32, _) = nsec3.name.split_once('.').unwrap();
1779 let start_hash = crate::base32::decode(start_hash_base32).unwrap();
1780 if start_hash[..] > nsec3.next_name_hash[..] { wrapping_nsec3s += 1; }
1781 }
1782 assert_eq!(wrapping_nsec3s, 1, "fixture no longer covers the NSEC3 wrap-around case");
1783
1784 rrs.shuffle(&mut rand::rngs::OsRng);
1785 let verified_rrs = verify_rr_stream(&rrs).unwrap();
1786
1787 let name = "wildcard.x_domain_cname_wild.dnssec_proof_tests.bitcoin.ninja.";
1788 let filtered_rrs = verified_rrs.resolve_name(&name.try_into().unwrap());
1789 assert!(filtered_rrs.iter().any(|rr| if let RR::Txt(txt) = rr {
1790 txt.name.as_str() == "matt.user._bitcoin-payment.mattcorallo.com."
1791 && txt.data.as_vec().starts_with(b"bitcoin:")
1792 } else { false }));
1793 }
1794
1795 #[test]
1796 fn check_nsec3_matching_name_does_not_cover_it() {
1797 let mut rr_stream = Vec::new();
1801 for rr in root_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1802 for rr in ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1803 for rr in bitcoin_ninja_dnskey().1 { write_rr(&rr, 1, &mut rr_stream); }
1804 let (txt, txt_rrsig, nsec3, nsec3_rrsig) = bitcoin_ninja_cname_target_wildcard_record();
1805 for rr in [RR::Txt(txt), RR::RRSig(txt_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1806 for rr in [RR::NSec3(nsec3), RR::RRSig(nsec3_rrsig)] { write_rr(&rr, 1, &mut rr_stream); }
1807
1808 let rrs = parse_rr_stream(&rr_stream).unwrap();
1811 verify_rr_stream(&rrs).unwrap();
1812
1813 let overridden: Name =
1816 "override.wildcard_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
1817 let mut rrs: Vec<RR> = rrs.into_iter()
1818 .filter(|rr| rr.ty() != NSec3::TYPE)
1819 .filter(|rr| !matches!(rr, RR::RRSig(sig) if sig.ty == NSec3::TYPE))
1820 .map(|rr| match rr {
1821 RR::Txt(mut txt) => { txt.name = overridden.clone(); RR::Txt(txt) },
1822 RR::RRSig(mut sig) if sig.ty == Txt::TYPE => {
1823 sig.name = overridden.clone();
1824 RR::RRSig(sig)
1825 },
1826 rr => rr,
1827 })
1828 .collect();
1829
1830 let dnskeys = rrs.iter()
1833 .filter_map(|rr| if let RR::DnsKey(k) = rr { Some(k) } else { None })
1834 .filter(|k| k.name.as_str() == "bitcoin.ninja.")
1835 .collect::<Vec<_>>();
1836 let txt = rrs.iter()
1837 .find_map(|rr| if let RR::Txt(txt) = rr { Some(txt) } else { None }).unwrap();
1838 let txt_rrsig = rrs.iter()
1839 .find_map(|rr| if let RR::RRSig(sig) = rr {
1840 if sig.ty == Txt::TYPE { Some(sig) } else { None }
1841 } else { None }).unwrap();
1842 assert_eq!(txt.name, overridden);
1843 verify_rrsig(txt_rrsig, dnskeys.iter().copied(), vec![txt]).unwrap();
1844
1845 let (nsec3, nsec3_rrsig) = bitcoin_ninja_override_matching_nsec3(&dnskeys);
1848 rrs.push(RR::NSec3(nsec3));
1849 rrs.push(RR::RRSig(nsec3_rrsig));
1850 rrs.shuffle(&mut rand::rngs::OsRng);
1851 assert_eq!(verify_rr_stream(&rrs).unwrap_err(), ValidationError::Invalid);
1852 }
1853
1854}