fix RRSIG and SIG usage with new RecordData types

This commit is contained in:
Benjamin Fry 2022-12-30 12:29:12 -08:00
parent f705256c55
commit 9e56c123c2
15 changed files with 184 additions and 141 deletions

View File

@ -17,7 +17,7 @@ use tokio::runtime::Runtime;
use tracing::{info, warn};
use trust_dns_client::client::*;
use trust_dns_client::proto::xfer::DnsResponse;
use trust_dns_client::rr::*;
use trust_dns_proto::rr::*;
#[cfg(feature = "dnssec")]
use trust_dns_proto::rr::{dnssec::rdata::DNSSECRData, dnssec::*};
@ -266,6 +266,8 @@ pub fn query_all_dnssec(
algorithm: Algorithm,
with_rfc6975: bool,
) {
use trust_dns_client::rr::rdata::{DNSKEY, RRSIG};
let name = Name::from_str("example.com.").unwrap();
let mut client = MutMessageHandle::new(client);
client.lookup_options.set_is_dnssec(true);
@ -280,14 +282,8 @@ pub fn query_all_dnssec(
let dnskey = response
.answers()
.iter()
.filter(|r| r.record_type() == RecordType::DNSKEY)
.map(|r| {
if let Some(RData::DNSSEC(DNSSECRData::DNSKEY(ref dnskey))) = r.data() {
dnskey.clone()
} else {
panic!("wrong RDATA")
}
})
.filter_map(|r| r.data())
.filter_map(|r| DNSKEY::try_borrow(r).ok())
.find(|d| d.algorithm() == algorithm);
assert!(dnskey.is_some(), "DNSKEY not found");
@ -296,14 +292,8 @@ pub fn query_all_dnssec(
let rrsig = response
.answers()
.iter()
.filter(|r| r.record_type() == RecordType::RRSIG)
.map(|r| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = r.data() {
rrsig.clone()
} else {
panic!("wrong RDATA")
}
})
.filter_map(|r| r.data())
.filter_map(|r| RRSIG::try_borrow(r).ok())
.filter(|rrsig| rrsig.algorithm() == algorithm)
.find(|rrsig| rrsig.type_covered() == RecordType::DNSKEY);
assert!(rrsig.is_some(), "Associated RRSIG not found");

View File

@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
use crate::{
error::ProtoResult,
rr::{RData, RecordData, RecordDataDecodable, RecordType},
rr::{dnssec::Algorithm, Name, RData, RecordData, RecordDataDecodable, RecordType},
serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict},
};
@ -25,6 +25,52 @@ use super::{DNSSECRData, SIG};
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct RRSIG(SIG);
impl RRSIG {
/// Creates a new SIG record data, used for both RRSIG and SIG(0) records.
///
/// # Arguments
///
/// * `type_covered` - The `RecordType` which this signature covers, should be NULL for SIG(0).
/// * `algorithm` - The `Algorithm` used to generate the `signature`.
/// * `num_labels` - The number of labels in the name, should be less 1 for *.name labels,
/// see `Name::num_labels()`.
/// * `original_ttl` - The TTL for the RRSet stored in the zone, should be 0 for SIG(0).
/// * `sig_expiration` - Timestamp at which this signature is no longer valid, very important to
/// keep this low, < +5 minutes to limit replay attacks.
/// * `sig_inception` - Timestamp when this signature was generated.
/// * `key_tag` - See the key_tag generation in `rr::dnssec::Signer::key_tag()`.
/// * `signer_name` - Domain name of the server which was used to generate the signature.
/// * `sig` - signature stored in this record.
///
/// # Return value
///
/// The new SIG record data.
#[allow(clippy::too_many_arguments)]
pub fn new(
type_covered: RecordType,
algorithm: Algorithm,
num_labels: u8,
original_ttl: u32,
sig_expiration: u32,
sig_inception: u32,
key_tag: u16,
signer_name: Name,
sig: Vec<u8>,
) -> Self {
Self(SIG::new(
type_covered,
algorithm,
num_labels,
original_ttl,
sig_expiration,
sig_inception,
key_tag,
signer_name,
sig,
))
}
}
impl Deref for RRSIG {
type Target = SIG;

View File

@ -595,7 +595,7 @@ mod tests {
use crate::op::{Message, Query};
use crate::rr::dnssec::rdata::key::KeyUsage;
use crate::rr::dnssec::rdata::{DNSSECRData, SIG};
use crate::rr::dnssec::rdata::{DNSSECRData, RRSIG, SIG};
use crate::rr::dnssec::*;
use crate::rr::rdata::NS;
use crate::rr::{DNSClass, Name, Record, RecordType};
@ -677,9 +677,9 @@ mod tests {
let rrsig = Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
.set_data(Some(RRSIG::new(
RecordType::NS,
Algorithm::RSASHA256,
origin.num_labels(),
@ -689,7 +689,7 @@ mod tests {
signer.calculate_key_tag().unwrap(),
origin.clone(),
vec![],
)))))
)))
.clone();
let rrset = vec![
Record::new()
@ -799,7 +799,7 @@ MC0CAQACBQC+L6pNAgMBAAECBQCYj0ZNAgMA9CsCAwDHZwICeEUCAnE/AgMA3u0=
mod tests {
use openssl::rsa::Rsa;
use crate::rr::dnssec::rdata::{DNSSECRData, SIG};
use crate::rr::dnssec::rdata::RRSIG;
use crate::rr::dnssec::tbs::*;
use crate::rr::dnssec::*;
use crate::rr::rdata::{CNAME, NS};
@ -816,9 +816,9 @@ MC0CAQACBQC+L6pNAgMBAAECBQCYj0ZNAgMA9CsCAwDHZwICeEUCAnE/AgMA3u0=
let rrsig = Record::new()
.set_name(origin.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
.set_data(Some(RRSIG::new(
RecordType::NS,
Algorithm::RSASHA256,
origin.num_labels(),
@ -828,7 +828,7 @@ MC0CAQACBQC+L6pNAgMBAAECBQCYj0ZNAgMA9CsCAwDHZwICeEUCAnE/AgMA3u0=
signer.calculate_key_tag().unwrap(),
origin.clone(),
vec![],
)))))
)))
.clone();
let rrset = vec![
Record::new()

View File

@ -1,9 +1,9 @@
//! hash functions for DNSSEC operations
use super::rdata::{sig, DNSSECRData, SIG};
use super::rdata::{sig, RRSIG, SIG};
use crate::error::*;
use crate::rr::dnssec::Algorithm;
use crate::rr::{DNSClass, Name, RData, Record, RecordType};
use crate::rr::{DNSClass, Name, Record, RecordType};
use crate::serialize::binary::{BinEncodable, BinEncoder, EncodeMode};
/// Data To Be Signed.
@ -183,8 +183,8 @@ pub fn rrset_tbs(
/// # Return
///
/// binary hash of the RRSet with the information from the RRSIG record
pub fn rrset_tbs_with_rrsig(rrsig: &Record, records: &[Record]) -> ProtoResult<TBS> {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = rrsig.data() {
pub fn rrset_tbs_with_rrsig(rrsig: &Record<RRSIG>, records: &[Record]) -> ProtoResult<TBS> {
if let Some(sig) = rrsig.data() {
rrset_tbs_with_sig(rrsig.name(), rrsig.dns_class(), sig, records)
} else {
Err(format!("could not determine name from {}", rrsig.name()).into())

View File

@ -1,7 +1,7 @@
//! Verifier is a structure for performing many of the signing processes of the DNSSEC specification
use crate::error::*;
use crate::rr::dnssec::rdata::{DNSKEY, KEY, SIG};
use crate::rr::dnssec::rdata::{DNSKEY, KEY, RRSIG, SIG};
use crate::rr::dnssec::Algorithm;
use crate::rr::dnssec::{tbs, PublicKey, PublicKeyEnum};
use crate::rr::{DNSClass, Name, Record};
@ -62,7 +62,7 @@ pub trait Verifier {
&self,
name: &Name,
dns_class: DNSClass,
sig: &SIG,
sig: &RRSIG,
records: &[Record],
) -> ProtoResult<()> {
let rrset_tbs = tbs::rrset_tbs_with_sig(name, dns_class, sig, records)?;

View File

@ -55,6 +55,7 @@ pub trait RecordData: Clone + Sized + PartialEq + Eq + fmt::Display + BinEncodab
fn try_from_rdata(data: RData) -> Result<Self, RData>;
/// Attempts to borrow this RecordData from the RData type, if it is not the correct type the original is returned
/// FIXME: make this return Option instead of Result
fn try_borrow(data: &RData) -> Result<&Self, &RData>;
/// Get the associated RecordType for the RData

View File

@ -19,8 +19,8 @@ use enum_as_inner::EnumAsInner;
use tracing::{trace, warn};
use super::rdata::{
ANAME, CAA, CNAME, CSYNC, HINFO, MX, NAPTR, NS, NULL, OPENPGPKEY, OPT, PTR, SOA, SRV, SSHFP,
SVCB, TLSA, TXT,
ANAME, CAA, CNAME, CSYNC, HINFO, HTTPS, MX, NAPTR, NS, NULL, OPENPGPKEY, OPT, PTR, SOA, SRV,
SSHFP, SVCB, TLSA, TXT,
};
use super::record_type::RecordType;
use super::{RecordData, RecordDataDecodable};
@ -232,7 +232,7 @@ pub enum RData {
///
/// Name TTL IN HTTPS SvcPriority TargetName SvcParams
/// ```
HTTPS(SVCB),
HTTPS(HTTPS),
/// ```text
/// 3.3.9. MX RDATA format
@ -827,7 +827,7 @@ impl BinEncodable for RData {
Self::PTR(ref ptr) => ptr.emit(encoder),
Self::CSYNC(ref csync) => csync.emit(encoder),
Self::HINFO(ref hinfo) => hinfo.emit(encoder),
Self::HTTPS(ref svcb) => svcb.emit(encoder),
Self::HTTPS(ref https) => https.emit(encoder),
Self::ZERO => Ok(()),
Self::MX(ref mx) => mx.emit(encoder),
Self::NAPTR(ref naptr) => encoder.with_canonical_names(|encoder| naptr.emit(encoder)),
@ -891,7 +891,7 @@ impl<'r> RecordDataDecodable<'r> for RData {
}
RecordType::HTTPS => {
trace!("reading HTTPS");
SVCB::read_data(decoder, record_type, length).map(Self::HTTPS)
HTTPS::read_data(decoder, record_type, length).map(Self::HTTPS)
}
RecordType::ZERO => {
trace!("reading EMPTY");
@ -1014,7 +1014,7 @@ impl fmt::Display for RData {
Self::PTR(ref ptr) => w(f, ptr),
Self::CSYNC(ref csync) => w(f, csync),
Self::HINFO(ref hinfo) => w(f, hinfo),
Self::HTTPS(ref svcb) => w(f, svcb),
Self::HTTPS(ref https) => w(f, https),
Self::ZERO => Ok(()),
// to_lowercase for rfc4034 and rfc6840
Self::MX(ref mx) => w(f, mx),

View File

@ -280,7 +280,19 @@ impl<R: RecordData> Record<R> {
/// For example, the if the TYPE is A and the CLASS is IN,
/// the RDATA field is a 4 octet ARPA Internet address.
/// ```
#[track_caller]
pub fn set_data(&mut self, rdata: Option<R>) -> &mut Self {
debug_assert!(
if let Some(rdata) = &rdata {
rdata.record_type() == self.record_type() || rdata.record_type() == RecordType::NULL
} else {
true
},
"record types do not match, {} <> {:?}",
self.record_type(),
rdata.map(|r| r.record_type())
);
self.rdata = rdata;
self
}
@ -538,6 +550,17 @@ impl<'r, R: RecordData + RecordDataDecodable<'r>> BinDecodable<'r> for Record<R>
)?)
};
debug_assert!(
if let Some(rdata) = &rdata {
rdata.record_type() == record_type
} else {
true
},
"record types do not match, {} <> {:?}",
record_type,
rdata.map(|r| r.record_type())
);
Ok(Self {
name_labels,
rr_type: record_type,

View File

@ -562,14 +562,14 @@ impl<'r> Iterator for RrsigsByAlgorithms<'r> {
self.rrsigs
.by_ref()
.filter(|record| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = record.data() {
if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref rrsig))) = record.data() {
supported_algorithms.has(rrsig.algorithm())
} else {
false
}
})
.max_by_key(|record| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = record.data() {
if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref rrsig))) = record.data() {
rrsig.algorithm()
} else {
#[allow(deprecated)]
@ -870,11 +870,11 @@ mod test {
#[allow(clippy::blocks_in_if_conditions)]
fn test_get_filter() {
use crate::rr::dnssec::rdata::DNSSECRData;
use crate::rr::dnssec::rdata::SIG;
use crate::rr::dnssec::rdata::RRSIG;
use crate::rr::dnssec::{Algorithm, SupportedAlgorithms};
let name = Name::root();
let rsasha256 = SIG::new(
let rsasha256 = RRSIG::new(
RecordType::A,
Algorithm::RSASHA256,
0,
@ -885,7 +885,7 @@ mod test {
Name::root(),
vec![],
);
let ecp256 = SIG::new(
let ecp256 = RRSIG::new(
RecordType::A,
Algorithm::ECDSAP256SHA256,
0,
@ -896,7 +896,7 @@ mod test {
Name::root(),
vec![],
);
let ecp384 = SIG::new(
let ecp384 = RRSIG::new(
RecordType::A,
Algorithm::ECDSAP384SHA384,
0,
@ -907,7 +907,7 @@ mod test {
Name::root(),
vec![],
);
let ed25519 = SIG::new(
let ed25519 = RRSIG::new(
RecordType::A,
Algorithm::ED25519,
0,
@ -924,28 +924,28 @@ mod test {
.set_ttl(3600)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(rsasha256))))
.set_data(Some(RData::DNSSEC(DNSSECRData::RRSIG(rsasha256))))
.clone();
let rrsig_ecp256 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ecp256))))
.set_data(Some(RData::DNSSEC(DNSSECRData::RRSIG(ecp256))))
.clone();
let rrsig_ecp384 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ecp384))))
.set_data(Some(RData::DNSSEC(DNSSECRData::RRSIG(ecp384))))
.clone();
let rrsig_ed25519 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::RRSIG)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ed25519))))
.set_data(Some(RData::DNSSEC(DNSSECRData::RRSIG(ed25519))))
.clone();
let a = Record::new()
@ -965,7 +965,7 @@ mod test {
assert!(rrset
.records_with_rrsigs(SupportedAlgorithms::all(),)
.any(
|r| if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
|r| if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref sig))) = r.data() {
sig.algorithm() == Algorithm::ED25519
} else {
false
@ -975,7 +975,7 @@ mod test {
let mut supported_algorithms = SupportedAlgorithms::new();
supported_algorithms.set(Algorithm::ECDSAP384SHA384);
assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref sig))) = r.data() {
sig.algorithm() == Algorithm::ECDSAP384SHA384
} else {
false
@ -985,7 +985,7 @@ mod test {
let mut supported_algorithms = SupportedAlgorithms::new();
supported_algorithms.set(Algorithm::ED25519);
assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref sig))) = r.data() {
sig.algorithm() == Algorithm::ED25519
} else {
false

View File

@ -20,7 +20,7 @@
use crate::rr::dnssec::rdata::DNSSECRData;
use crate::{
rr::{
rdata::{ANAME, CNAME, NS, PTR},
rdata::{ANAME, CNAME, HTTPS, NS, PTR},
Name, RData, RecordType,
},
serialize::txt::{
@ -81,7 +81,7 @@ impl RDataParser for RData {
RecordType::CNAME => Self::CNAME(CNAME(name::parse(tokens, origin)?)),
RecordType::CSYNC => csync::parse(tokens).map(Self::CSYNC)?,
RecordType::HINFO => Self::HINFO(hinfo::parse(tokens)?),
RecordType::HTTPS => svcb::parse(tokens).map(Self::SVCB)?,
RecordType::HTTPS => svcb::parse(tokens).map(HTTPS).map(Self::HTTPS)?,
RecordType::IXFR => return Err(ParseError::from("parsing IXFR doesn't make sense")),
RecordType::MX => Self::MX(mx::parse(tokens, origin)?),
RecordType::NAPTR => Self::NAPTR(naptr::parse(tokens, origin)?),

View File

@ -1,4 +1,4 @@
// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
// Copyright 2015-2022 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
@ -20,12 +20,12 @@ use futures_util::stream::{Stream, TryStreamExt};
use tracing::{debug, trace};
use crate::op::{OpCode, Query};
use crate::rr::dnssec::rdata::{DNSSECRData, DNSKEY, SIG};
use crate::rr::dnssec::rdata::{DNSSECRData, DNSKEY, RRSIG};
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::Verifier;
use crate::rr::dnssec::{Algorithm, SupportedAlgorithms, TrustAnchor};
use crate::rr::rdata::opt::EdnsOption;
use crate::rr::{DNSClass, Name, RData, Record, RecordType};
use crate::rr::{DNSClass, Name, RData, Record, RecordData, RecordType};
use crate::xfer::dns_handle::DnsHandle;
use crate::xfer::{DnsRequest, DnsRequestOptions, DnsResponse, FirstAnswer};
use crate::{error::*, op::Edns};
@ -282,20 +282,21 @@ where
.cloned()
.collect();
let rrsigs: Vec<Record> = message_result
let rrsigs: Vec<Record<RRSIG>> = message_result
.answers()
.iter()
.chain(message_result.name_servers())
.chain(message_result.additionals())
.filter(|rr| is_dnssec(rr, RecordType::RRSIG))
.filter(|rr| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = rr.data() {
if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref rrsig))) = rr.data() {
rrsig.type_covered() == record_type
} else {
false
}
})
.cloned()
.map(|rr| Record::<RRSIG>::try_from(rr).expect("the record type was checked above"))
.collect();
// if there is already an active validation going on, assume the other validation will
@ -322,7 +323,8 @@ where
verify_all_rrsets(message_result, rrsets_to_verify).await
}
fn is_dnssec(rr: &Record, dnssec_type: RecordType) -> bool {
// TODO: is this method useful/necessary?
fn is_dnssec<D: RecordData>(rr: &Record<D>, dnssec_type: RecordType) -> bool {
rr.record_type().is_dnssec() && dnssec_type.is_dnssec() && rr.record_type() == dnssec_type
}
@ -428,7 +430,7 @@ where
async fn verify_rrset<H, E>(
handle: DnssecDnsHandle<H>,
rrset: Rrset,
rrsigs: Vec<Record>,
rrsigs: Vec<Record<RRSIG>>,
options: DnsRequestOptions,
) -> Result<Rrset, E>
where
@ -484,13 +486,8 @@ where
.iter()
.enumerate()
.filter(|&(_, rr)| is_dnssec(rr, RecordType::DNSKEY))
.filter_map(|(i, rr)| {
if let Some(RData::DNSSEC(DNSSECRData::DNSKEY(ref rdata))) = rr.data() {
Some((i, rdata))
} else {
None
}
})
.filter_map(|(i, rr)| rr.data().map(|rr| (i, rr)))
.filter_map(|(i, rr)| DNSKEY::try_borrow(rr).map(|rr| (i, rr)).ok())
.filter_map(|(i, rdata)| {
if handle
.trust_anchor
@ -642,7 +639,7 @@ fn test_preserve() {
async fn verify_default_rrset<H, E>(
handle: &DnssecDnsHandle<H>,
rrset: Rrset,
rrsigs: Vec<Record>,
rrsigs: Vec<Record<RRSIG>>,
options: DnsRequestOptions,
) -> Result<Rrset, E>
where
@ -661,13 +658,8 @@ where
if rrsigs
.iter()
.filter(|rrsig| is_dnssec(rrsig, RecordType::RRSIG))
.any(|rrsig| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = rrsig.data() {
RecordType::DNSKEY == rrset.record_type && sig.signer_name() == &rrset.name
} else {
panic!("expected a SIG here");
}
})
.filter_map(|rrsig| rrsig.data())
.any(|rrsig| RecordType::DNSKEY == rrset.record_type && rrsig.signer_name() == &rrset.name)
{
// in this case it was looks like a self-signed key, first validate the signature
// then return rrset. Like the standard case below, the DNSKEY is validated
@ -678,14 +670,7 @@ where
.into_iter()
// this filter is technically unnecessary, can probably remove it...
.filter(|rrsig| is_dnssec(rrsig, RecordType::RRSIG))
.map(|rrsig| {
if let Some(RData::DNSSEC(DNSSECRData::SIG(sig))) = rrsig.into_data() {
// setting up the context explicitly.
sig
} else {
panic!("expected a SIG here");
}
})
.filter_map(|rrsig| rrsig.into_data())
.filter_map(|sig| {
let rrset = Arc::clone(&rrset);
@ -725,14 +710,7 @@ where
let verifications = rrsigs.into_iter()
// this filter is technically unnecessary, can probably remove it...
.filter(|rrsig| is_dnssec(rrsig, RecordType::RRSIG))
.map(|rrsig|
if let Some(RData::DNSSEC(DNSSECRData::SIG(sig))) = rrsig.into_data() {
// setting up the context explicitly.
sig
} else {
panic!("expected a SIG here");
}
)
.filter_map(|rrsig|rrsig.into_data())
.map(|sig| {
let rrset = Arc::clone(&rrset);
let mut handle = handle.clone_with_context();
@ -749,13 +727,11 @@ where
.answers()
.iter()
.filter(|r| is_dnssec(r, RecordType::DNSKEY))
.find(|r|
if let Some(RData::DNSSEC(DNSSECRData::DNSKEY(ref dnskey))) = r.data() {
let dnskey_name = r.name();
.filter_map(|r| r.data().map(|data| (r.name(), data)))
.filter_map(|(dnskey_name, data)|
DNSKEY::try_borrow(data).ok().map(|data| (dnskey_name, data)))
.find(|(dnskey_name, dnskey)|
verify_rrset_with_dnskey(dnskey_name, dnskey, &sig, &rrset).is_ok()
} else {
panic!("expected a DNSKEY here: {:?}", r.data());
}
)
.map(|_| ())
.ok_or_else(|| E::from(ProtoError::from(ProtoErrorKind::Message("validation failed")))))
@ -789,7 +765,7 @@ where
fn verify_rrset_with_dnskey(
dnskey_name: &Name,
dnskey: &DNSKEY,
sig: &SIG,
sig: &RRSIG,
rrset: &Rrset,
) -> ProtoResult<()> {
if dnskey.revoke() {
@ -824,7 +800,7 @@ fn verify_rrset_with_dnskey(
/// Will always return an error. To enable record verification compile with the openssl feature.
#[cfg(not(feature = "dnssec"))]
fn verify_rrset_with_dnskey(_: &DNSKEY, _: &SIG, _: &Rrset) -> ProtoResult<()> {
fn verify_rrset_with_dnskey(_: &DNSKEY, _: &RRSIG, _: &Rrset) -> ProtoResult<()> {
Err(ProtoErrorKind::Message("openssl or ring feature(s) not enabled").into())
}

View File

@ -251,6 +251,8 @@ mod tests {
use std::net::Ipv4Addr;
use std::str::FromStr;
use trust_dns_client::rr::RecordType;
use crate::proto::op::{Header, Message};
use crate::proto::rr::{DNSClass, Name, RData, Record};
use crate::proto::serialize::binary::BinEncoder;
@ -265,6 +267,7 @@ mod tests {
encoder.set_max_size(512);
let answer = Record::new()
.set_record_type(RecordType::A)
.set_name(Name::from_str("www.example.com.").unwrap())
.set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 34))))
.set_dns_class(DNSClass::NONE)
@ -301,6 +304,7 @@ mod tests {
encoder.set_max_size(512);
let answer = Record::new()
.set_record_type(RecordType::A)
.set_name(Name::from_str("www.example.com.").unwrap())
.set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 34))))
.set_dns_class(DNSClass::NONE)

View File

@ -744,6 +744,10 @@ impl InnerInMemory {
zone_ttl: u32,
zone_class: DNSClass,
) -> DnsSecResult<()> {
use crate::client::rr::dnssec::tbs;
use time::OffsetDateTime;
use trust_dns_client::rr::rdata::RRSIG;
let inception = OffsetDateTime::now_utc();
rr_set.clear_rrsigs();
@ -798,7 +802,7 @@ impl InnerInMemory {
};
let mut rrsig = rrsig_temp.clone();
rrsig.set_data(Some(RData::DNSSEC(DNSSECRData::SIG(SIG::new(
rrsig.set_data(Some(RData::DNSSEC(DNSSECRData::RRSIG(RRSIG::new(
// type_covered: RecordType,
rr_set.record_type(),
// algorithm: Algorithm,

View File

@ -5,13 +5,19 @@ use std::str::FromStr;
use futures_executor::block_on;
use trust_dns_proto::op::{Header, Query};
use trust_dns_proto::rr::dnssec::rdata::DNSKEY;
use trust_dns_proto::rr::dnssec::{Algorithm, SupportedAlgorithms, Verifier};
use trust_dns_proto::rr::{DNSClass, Name, RData, Record, RecordType};
use trust_dns_proto::xfer;
use trust_dns_server::authority::{AuthLookup, Authority, DnssecAuthority, LookupOptions};
use trust_dns_server::server::{Protocol, RequestInfo};
use trust_dns_proto::{
op::{Header, Query},
rr::{
dnssec::{rdata::DNSKEY, Algorithm, SupportedAlgorithms, Verifier},
rdata::RRSIG,
DNSClass, Name, RData, Record, RecordType,
},
xfer,
};
use trust_dns_server::{
authority::{AuthLookup, Authority, DnssecAuthority, LookupOptions},
server::{Protocol, RequestInfo},
};
const TEST_HEADER: &Header = &Header::new();
@ -35,9 +41,10 @@ pub fn test_a_lookup<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DN
.cloned()
.partition(|r| r.record_type() == RecordType::A);
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&a_records, &rrsig_records, keys);
@ -71,9 +78,10 @@ pub fn test_soa<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY]
assert_eq!(604800, soa.expire());
assert_eq!(86400, soa.minimum());
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&soa_records, &rrsig_records, keys);
@ -100,9 +108,10 @@ pub fn test_ns<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DNSKEY])
Name::from_str("bbb.example.com.").unwrap()
);
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&ns_records, &rrsig_records, keys);
@ -132,9 +141,10 @@ pub fn test_aname_lookup<A: Authority<Lookup = AuthLookup>>(authority: A, keys:
.cloned()
.partition(|r| r.record_type() == RecordType::A);
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&a_records, &rrsig_records, keys);
@ -169,9 +179,10 @@ pub fn test_wildcard<A: Authority<Lookup = AuthLookup>>(authority: A, keys: &[DN
.iter()
.all(|r| *r.name() == Name::from_str("www.wildcard.example.com.").unwrap()));
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&cname_records, &rrsig_records, keys);
@ -331,16 +342,17 @@ pub fn test_rfc_6975_supported_algorithms<A: Authority<Lookup = AuthLookup>>(
.cloned()
.partition(|r| r.record_type() == RecordType::A);
let (rrsig_records, _other_records): (Vec<_>, Vec<_>) = other_records
let rrsig_records: Vec<_> = other_records
.into_iter()
.partition(|r| r.record_type() == RecordType::RRSIG);
.filter_map(|r| Record::<RRSIG>::try_from(r).ok())
.collect();
assert!(!rrsig_records.is_empty());
verify(&a_records, &rrsig_records, &[key.clone()]);
}
}
pub fn verify(records: &[Record], rrsig_records: &[Record], keys: &[DNSKEY]) {
pub fn verify(records: &[Record], rrsig_records: &[Record<RRSIG>], keys: &[DNSKEY]) {
let record_name = records.first().unwrap().name();
let record_type = records.first().unwrap().record_type();
println!("record_name: {record_name}, type: {record_type}");
@ -348,19 +360,8 @@ pub fn verify(records: &[Record], rrsig_records: &[Record], keys: &[DNSKEY]) {
// should be signed with all the keys
assert!(keys.iter().all(|key| rrsig_records
.iter()
.filter_map(|rrsig| {
let rrsig = rrsig
.data()
.and_then(RData::as_dnssec)
.expect("not DNSSEC")
.as_sig()
.expect("not RRSIG");
if rrsig.algorithm() == key.algorithm() {
Some(rrsig)
} else {
None
}
})
.filter_map(|rrsig| rrsig.data())
.filter(|rrsig| rrsig.algorithm() == key.algorithm())
.filter(|rrsig| rrsig.key_tag() == key.calculate_key_tag().unwrap())
.filter(|rrsig| rrsig.type_covered() == record_type)
.any(|rrsig| key

View File

@ -961,13 +961,11 @@ async fn test_zone_signing() {
assert!(
inner_results
.iter()
.any(|r| r.record_type() == RecordType::RRSIG
&& r.name() == record.name()
&& if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *r.data().unwrap() {
rrsig.type_covered() == record.record_type()
} else {
false
}),
.filter(|r| r.record_type() == RecordType::RRSIG)
.filter(|r| r.name() == record.name())
.filter_map(|r| r.data())
.filter_map(|r| RRSIG::try_borrow(r).ok())
.any(|rrsig| rrsig.type_covered() == record.record_type()),
"record type not covered: {:?}",
record
);