diff --git a/Cargo.toml b/Cargo.toml index 7b749dd74..4c7c24e2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ name = "domain" path = "src/lib.rs" [dependencies] -octseq = "0.2" +octseq = { git = "https://github.com/nlnetlabs/octseq.git" } time = "0.3.1" rand = { version = "0.8", optional = true } diff --git a/src/base/rdata.rs b/src/base/rdata.rs index aee58d885..8fe224262 100644 --- a/src/base/rdata.rs +++ b/src/base/rdata.rs @@ -55,7 +55,7 @@ impl<'a, T: RecordData> RecordData for &'a T { //----------- ComposeRecordData ---------------------------------------------- /// A type of record data that can be composed. -pub trait ComposeRecordData: RecordData { +pub trait ComposeRecordData { /// Returns the length of the record data if available. /// /// The method should return `None`, if the length is not known or is not diff --git a/src/base/record.rs b/src/base/record.rs index ced03ee56..7c2a0d2b4 100644 --- a/src/base/record.rs +++ b/src/base/record.rs @@ -436,7 +436,7 @@ impl<'a, T: ComposeRecord> ComposeRecord for &'a T { impl ComposeRecord for Record where Name: ToDname, - Data: ComposeRecordData, + Data: RecordData + ComposeRecordData, { fn compose_record( &self, @@ -449,7 +449,7 @@ where impl ComposeRecord for (Name, Class, u32, Data) where Name: ToDname, - Data: ComposeRecordData, + Data: RecordData + ComposeRecordData, { fn compose_record( &self, @@ -463,7 +463,7 @@ where impl ComposeRecord for (Name, Class, Ttl, Data) where Name: ToDname, - Data: ComposeRecordData, + Data: RecordData + ComposeRecordData, { fn compose_record( &self, @@ -476,7 +476,7 @@ where impl ComposeRecord for (Name, u32, Data) where Name: ToDname, - Data: ComposeRecordData, + Data: RecordData + ComposeRecordData, { fn compose_record( &self, @@ -490,7 +490,7 @@ where impl ComposeRecord for (Name, Ttl, Data) where Name: ToDname, - Data: ComposeRecordData, + Data: RecordData + ComposeRecordData, { fn compose_record( &self, diff --git a/src/lib.rs b/src/lib.rs index 3a6f5eb61..32f5d45eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ #![allow(clippy::uninlined_format_args)] #![cfg_attr(docsrs, feature(doc_cfg))] -#[cfg(any(feature = "std"))] +#[cfg(feature = "std")] #[allow(unused_imports)] // Import macros even if unused. #[macro_use] extern crate std; diff --git a/src/sign/generic.rs b/src/sign/generic.rs new file mode 100644 index 000000000..53ab7ec6b --- /dev/null +++ b/src/sign/generic.rs @@ -0,0 +1,140 @@ +use octseq::builder::{ + BuilderAppendError, EmptyBuilder, FromBuilder, Truncate, +}; +use crate::base::iana::{Class, Rtype}; +use crate::base::name::ToDname; +use crate::base::record::{Record, Ttl}; +use crate::base::rdata::ComposeRecordData; +use crate::base::serial::Serial; +use crate::rdata::dnssec::{Ds, Dnskey, Nsec, ProtoRrsig, Rrsig, RtypeBitmap}; +use super::key::SigningKey; + + +pub trait Rrset { + type Owner: ToDname; + type RecordData<'a>: ComposeRecordData where Self: 'a; + type Iter<'a>: Iterator> where Self: 'a; + + fn owner(&self) -> &Self::Owner; + fn class(&self) -> Class; + fn rtype(&self) -> Rtype; + fn ttl(&self) -> Ttl; + fn iter(&self) -> Self::Iter<'_>; + + fn sign( + &self, + apex: Name, + expiration: Serial, + inception: Serial, + key: Key, + ) -> Result, Key::Error> + where + Octs: From<::Signature> + AsRef<[u8]>, + Name: ToDname, + Key: SigningKey, + { + let rrsig = ProtoRrsig::new( + self.rtype(), + key.algorithm()?, + self.owner().rrsig_label_count(), + self.ttl(), + expiration, + inception, + key.key_tag()?, + apex, + ); + let sig = key.sign(|buf| { + rrsig.compose_canonical(buf)?; + for item in self.iter() { + // We can’t use Record because it wants its rtype in the Data + // type. Luckily, composing a record is straighforward: + self.owner().compose_canonical(buf)?; + self.rtype().compose(buf)?; + self.class().compose(buf)?; + self.ttl().compose(buf)?; + item.compose_canonical_len_rdata(buf)?; + } + Ok(()) + })?; + Ok(rrsig.into_rrsig(Octs::from(sig)).expect("long signature")) + } +} + +pub trait Rrfamily { + type Owner: ToDname; + type Rrset<'a>: Rrset where Self: 'a; + type Iter<'a>: Iterator> where Self: 'a; + + fn owner(&self) -> &Self::Owner; + fn class(&self) -> Class; + fn iter(&self) -> Self::Iter<'_>; + + + //--- Things to do at the zone apex + + fn dnskey>( + &self, + ttl: Ttl, + key: K + ) -> Result>, K::Error> + where + Self::Owner: Clone + { + key.dnskey().map(|data| { + self.to_record(ttl, data.convert()) + }) + } + + #[allow(clippy::type_complexity)] + fn ds( + &self, + ttl: Ttl, + key: K, + ) -> Result>, K::Error> + where + Self::Owner: ToDname + Clone, + { + key.ds(self.owner()).map(|ds| { + self.to_record(ttl, ds) + }) + } + + + //--- Things to do everywhere + + fn to_record(&self, ttl: Ttl, data: D) -> Record + where + Self::Owner: Clone + { + Record::new(self.owner().clone(), self.class(), ttl, data) + } + + fn rtype_bitmap( + &self + ) -> Result, BuilderAppendError> + where + Octs: FromBuilder, + ::Builder: + EmptyBuilder + Truncate + AsRef<[u8]> + AsMut<[u8]>, + { + let mut bitmap = RtypeBitmap::::builder(); + // Assume there’s going to be an RRSIG. + bitmap.add(Rtype::Rrsig)?; + for rrset in self.iter() { + bitmap.add(rrset.rtype())?; + } + Ok(bitmap.finalize()) + } + + fn nsec( + &self, next_name: Name + ) -> Result, BuilderAppendError> + where + Octs: FromBuilder, + ::Builder: + EmptyBuilder + Truncate + AsRef<[u8]> + AsMut<[u8]>, + { + Ok(Nsec::new(next_name, self.rtype_bitmap()?)) + } +} + diff --git a/src/sign/key.rs b/src/sign/key.rs index 9362ef97c..286fb1850 100644 --- a/src/sign/key.rs +++ b/src/sign/key.rs @@ -1,9 +1,13 @@ +use octseq::OctetsBuilder; use crate::base::iana::SecAlg; use crate::base::name::ToDname; +use crate::base::wire::Composer; use crate::rdata::{Dnskey, Ds}; + pub trait SigningKey { type Octets: AsRef<[u8]>; + type Signer: Composer; type Signature: AsRef<[u8]>; type Error; @@ -21,11 +25,16 @@ pub trait SigningKey { self.dnskey().map(|dnskey| dnskey.key_tag()) } - fn sign(&self, data: &[u8]) -> Result; + fn sign(&self, op: F) -> Result + where + F: FnOnce( + &mut Self::Signer + ) -> Result<(), ::AppendError>; } impl<'a, K: SigningKey> SigningKey for &'a K { type Octets = K::Octets; + type Signer = K::Signer; type Signature = K::Signature; type Error = K::Error; @@ -47,7 +56,13 @@ impl<'a, K: SigningKey> SigningKey for &'a K { (*self).key_tag() } - fn sign(&self, data: &[u8]) -> Result { - (*self).sign(data) + fn sign(&self, op: F) -> Result + where + F: FnOnce( + &mut Self::Signer + ) -> Result<(), ::AppendError> + { + (*self).sign(op) } } + diff --git a/src/sign/mod.rs b/src/sign/mod.rs index d87acca0c..71c5f010f 100644 --- a/src/sign/mod.rs +++ b/src/sign/mod.rs @@ -8,3 +8,5 @@ pub mod key; //pub mod openssl; pub mod records; pub mod ring; + +pub mod generic; diff --git a/src/sign/records.rs b/src/sign/records.rs index 2324fe82a..c023bfb76 100644 --- a/src/sign/records.rs +++ b/src/sign/records.rs @@ -1,5 +1,7 @@ //! Actual signing. +use super::generic; +use super::generic::{Rrset as _, Rrfamily as _}; use super::key::SigningKey; use crate::base::cmp::CanonicalOrd; use crate::base::iana::{Class, Rtype}; @@ -8,7 +10,7 @@ use crate::base::rdata::{ComposeRecordData, RecordData}; use crate::base::record::Record; use crate::base::serial::Serial; use crate::base::Ttl; -use crate::rdata::dnssec::{ProtoRrsig, RtypeBitmap}; +use crate::rdata::dnssec::{RtypeBitmap}; use crate::rdata::{Dnskey, Ds, Nsec, Rrsig}; use octseq::builder::{EmptyBuilder, FromBuilder, OctetsBuilder, Truncate}; use std::iter::FromIterator; @@ -79,7 +81,6 @@ impl SortedRecords { ApexName: ToDname + Clone, { let mut res = Vec::new(); - let mut buf = Vec::new(); // The owner name of a zone cut if we currently are at or below one. let mut cut: Option> = None; @@ -133,31 +134,14 @@ impl SortedRecords { } } - // Create the signature. - buf.clear(); - let rrsig = ProtoRrsig::new( - rrset.rtype(), - key.algorithm()?, - name.owner().rrsig_label_count(), - rrset.ttl(), - expiration, - inception, - key.key_tag()?, - apex.owner().clone(), - ); - rrsig.compose_canonical(&mut buf).unwrap(); - for record in rrset.iter() { - record.compose_canonical(&mut buf).unwrap(); - } - // Create and push the RRSIG record. res.push(Record::new( name.owner().clone(), name.class(), rrset.ttl(), - rrsig - .into_rrsig(key.sign(&buf)?.into()) - .expect("long signature"), + rrset.sign( + apex.owner().clone(), expiration, inception, &key + )?, )); } } @@ -171,7 +155,7 @@ impl SortedRecords { ) -> Vec>> where N: ToDname + Clone, - D: RecordData, + D: RecordData + ComposeRecordData, Octets: FromBuilder, Octets::Builder: EmptyBuilder + Truncate + AsRef<[u8]> + AsMut<[u8]>, ::AppendError: fmt::Debug, @@ -228,14 +212,7 @@ impl SortedRecords { )); } - let mut bitmap = RtypeBitmap::::builder(); - // Assume there’s gonna be an RRSIG. - bitmap.add(Rtype::Rrsig).unwrap(); - for rrset in family.rrsets() { - bitmap.add(rrset.rtype()).unwrap() - } - - prev = Some((name, bitmap.finalize())); + prev = Some((name, family.rtype_bitmap().unwrap())); } if let Some((prev_name, bitmap)) = prev { res.push( @@ -351,6 +328,28 @@ impl<'a, N, D> Family<'a, N, D> { } } +impl<'a, N, D> generic::Rrfamily for Family<'a, N, D> +where + N: ToDname, + D: RecordData + ComposeRecordData, +{ + type Owner = N; + type Rrset<'s> = Rrset<'s, N, D> where 'a: 's, N: 's, D: 's; + type Iter<'s> = FamilyIter<'s, N, D> where 'a: 's, N: 's, D: 's; + + fn owner(&self) -> &Self::Owner { + self.slice[0].owner() + } + + fn class(&self) -> Class { + self.slice[0].class() + } + + fn iter(&self) -> Self::Iter<'_> { + self.rrsets() + } +} + //------------ FamilyName ---------------------------------------------------- /// The identifier for a family, i.e., a owner name and class. @@ -470,6 +469,54 @@ impl<'a, N, D> Rrset<'a, N, D> { } } +impl<'a, N, D> generic::Rrset for Rrset<'a, N, D> +where + N: ToDname, + D: RecordData + ComposeRecordData, +{ + type Owner = N; + type RecordData<'s> = &'s D where D: 's, 'a: 's; + type Iter<'s> = RecordDataIter<'s, N, D> where N: 's, D: 's, 'a: 's; + + fn owner(&self) -> &Self::Owner { + self.slice[0].owner() + } + + fn class(&self) -> Class { + self.slice[0].class() + } + + fn rtype(&self) -> Rtype { + self.slice[0].rtype() + } + + fn ttl(&self) -> Ttl { + self.slice[0].ttl() + } + + fn iter(&self) -> Self::Iter<'_> { + RecordDataIter { slice: self.slice } + } +} + + +//------------ RecordDataIter ------------------------------------------------ + +pub struct RecordDataIter<'a, N, D> { + slice: &'a [Record], +} + +impl<'a, N, D> Iterator for RecordDataIter<'a, N, D> { + type Item = &'a D; + + fn next(&mut self) -> Option { + let (res, slice) = self.slice.split_first()?; + self.slice = slice; + Some(res.data()) + } +} + + //------------ RecordsIter --------------------------------------------------- /// An iterator that produces families from sorted records. @@ -567,6 +614,7 @@ where } } + //------------ FamilyIter ---------------------------------------------------- /// An iterator that produces RRsets from a record family. diff --git a/src/sign/ring.rs b/src/sign/ring.rs index 1100d9477..fad2e62d0 100644 --- a/src/sign/ring.rs +++ b/src/sign/ring.rs @@ -9,7 +9,7 @@ use crate::base::rdata::ComposeRecordData; use crate::rdata::{Dnskey, Ds}; #[cfg(feature = "bytes")] use bytes::Bytes; -use octseq::builder::infallible; +use octseq::builder::{OctetsBuilder, infallible}; use ring::digest; use ring::error::Unspecified; use ring::rand::SecureRandom; @@ -62,6 +62,7 @@ impl<'a> Key<'a> { impl<'a> SigningKey for Key<'a> { type Octets = Vec; + type Signer = Vec; type Signature = Signature; type Error = Unspecified; @@ -87,15 +88,23 @@ impl<'a> SigningKey for Key<'a> { .expect("long digest")) } - fn sign(&self, msg: &[u8]) -> Result { + fn sign(&self, op: F) -> Result + where + F: FnOnce( + &mut Self::Signer + ) -> Result<(), ::AppendError> + { + let mut msg = Vec::new(); + infallible(op(&mut msg)); + match self.key { RingKey::Ecdsa(ref key) => { - key.sign(self.rng, msg).map(Signature::sig) + key.sign(self.rng, &msg).map(Signature::sig) } - RingKey::Ed25519(ref key) => Ok(Signature::sig(key.sign(msg))), + RingKey::Ed25519(ref key) => Ok(Signature::sig(key.sign(&msg))), RingKey::Rsa(ref key, encoding) => { let mut sig = vec![0; key.public_modulus_len()]; - key.sign(encoding, self.rng, msg, &mut sig)?; + key.sign(encoding, self.rng, &msg, &mut sig)?; Ok(Signature::vec(sig)) } }