From 5c1ab1f345f460a08d2d988c6a8021c4c7e26dd0 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 21 Sep 2017 19:13:45 -1000 Subject: [PATCH 1/9] Enable more digest-related functionality for *ring*. --- client/src/rr/dnssec/digest_type.rs | 70 +++++++++++++++++++++++++---- client/src/rr/dnssec/keypair.rs | 14 ++++-- client/src/rr/dnssec/mod.rs | 6 +++ client/src/rr/dnssec/nsec3.rs | 57 ++++++++++------------- client/src/rr/rdata/dnskey.rs | 22 ++++----- client/src/rr/rdata/ds.rs | 16 +++---- client/src/rr/rdata/key.rs | 2 +- 7 files changed, 120 insertions(+), 67 deletions(-) diff --git a/client/src/rr/dnssec/digest_type.rs b/client/src/rr/dnssec/digest_type.rs index 81b9abc87b..54db89ff31 100644 --- a/client/src/rr/dnssec/digest_type.rs +++ b/client/src/rr/dnssec/digest_type.rs @@ -15,12 +15,16 @@ */ #[cfg(feature = "openssl")] use openssl::hash; -#[cfg(feature = "openssl")] -use openssl::hash::{DigestBytes, MessageDigest}; + +#[cfg(feature = "ring")] +use ring::digest; use rr::dnssec::Algorithm; use error::*; +#[cfg(any(feature = "openssl", feature = "ring"))] +use super::Digest; + /// This is the digest format for the /// ///```text @@ -62,12 +66,12 @@ impl DigestType { /// The OpenSSL counterpart for the digest #[cfg(feature = "openssl")] - pub fn to_openssl_digest(&self) -> DnsSecResult { + pub fn to_openssl_digest(&self) -> DnsSecResult { match *self { - DigestType::SHA1 => Ok(MessageDigest::sha1()), - DigestType::SHA256 => Ok(MessageDigest::sha256()), - DigestType::SHA384 => Ok(MessageDigest::sha384()), - DigestType::SHA512 => Ok(MessageDigest::sha512()), + DigestType::SHA1 => Ok(hash::MessageDigest::sha1()), + DigestType::SHA256 => Ok(hash::MessageDigest::sha256()), + DigestType::SHA384 => Ok(hash::MessageDigest::sha384()), + DigestType::SHA512 => Ok(hash::MessageDigest::sha512()), _ => { Err(DnsSecErrorKind::Msg(format!("digest not supported by openssl: {:?}", self)) .into()) @@ -75,17 +79,65 @@ impl DigestType { } } + /// The *ring* counterpart for the digest + #[cfg(feature = "ring")] + pub fn to_ring_digest_alg(&self) -> DnsSecResult<&'static digest::Algorithm> { + match *self { + DigestType::SHA1 => Ok(&digest::SHA1), + DigestType::SHA256 => Ok(&digest::SHA256), + DigestType::SHA384 => Ok(&digest::SHA384), + DigestType::SHA512 => Ok(&digest::SHA512), + _ => + Err(DnsSecErrorKind::Msg(format!("digest not supported by ring: {:?}", self)) + .into()) + } + } + /// Hash the data #[cfg(feature = "openssl")] - pub fn hash(&self, data: &[u8]) -> DnsSecResult { + pub fn hash(&self, data: &[u8]) -> DnsSecResult { hash::hash2(try!(self.to_openssl_digest()), data).map_err(|e| e.into()) } + /// Hash the data + #[cfg(feature = "ring")] + pub fn hash(&self, data: &[u8]) -> DnsSecResult { + let alg = try!(self.to_ring_digest_alg()); + Ok(digest::digest(alg, data)) + } + /// This will always error, enable openssl feature at compile time - #[cfg(not(feature = "openssl"))] + #[cfg(not(any(feature = "openssl", feature = "ring")))] pub fn hash(&self, _: &[u8]) -> DnsSecResult> { Err(DnsSecErrorKind::Message("openssl feature not enabled").into()) } + + /// Digest all the data. + #[cfg(feature = "openssl")] + pub fn digest_all(&self, data: &[&[u8]]) -> DnsSecResult { + use std::io::Write; + + let digest_type = try!(self.to_openssl_digest()); + hash::Hasher::new(digest_type) + .map_err(|e| e.into()) + .and_then(|mut hasher| { + for d in data { + try!(hasher.write_all(d)); + } + hasher.finish2().map_err(|e| e.into()) + }) + } + + /// Digest all the data. + #[cfg(feature = "ring")] + pub fn digest_all(&self, data: &[&[u8]]) -> DnsSecResult { + let alg = try!(self.to_ring_digest_alg()); + let mut ctx = digest::Context::new(alg); + for d in data { + ctx.update(d); + } + Ok(ctx.finish()) + } } impl From for DigestType { diff --git a/client/src/rr/dnssec/keypair.rs b/client/src/rr/dnssec/keypair.rs index 60fbad0f12..f36749e731 100644 --- a/client/src/rr/dnssec/keypair.rs +++ b/client/src/rr/dnssec/keypair.rs @@ -24,13 +24,20 @@ use ring::rand; use ring::signature::Ed25519KeyPair; use error::*; +#[cfg(any(feature = "openssl", feature = "ring"))] use rr::Name; -use rr::dnssec::{Algorithm, DigestType, PublicKey}; +use rr::dnssec::{Algorithm, PublicKey}; +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::dnssec::DigestType; #[cfg(feature = "ring")] use rr::dnssec::public_key::Ed25519; #[cfg(feature = "openssl")] use rr::dnssec::public_key::dnssec_ecdsa_signature_to_der; -use rr::rdata::{DNSKEY, DS, KEY}; +use rr::rdata::{DNSKEY, KEY}; + +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::rdata::DS; + use rr::rdata::key::KeyUsage; /// A public and private key pair, the private portion is not required. @@ -278,6 +285,7 @@ impl KeyPair { /// * `name` - name of the DNSKEY record covered by the new DS record /// * `algorithm` - the algorithm of the DNSKEY /// * `digest_type` - the digest_type used to + #[cfg(any(feature = "openssl", feature = "ring"))] pub fn to_ds(&self, name: &Name, algorithm: Algorithm, @@ -290,7 +298,7 @@ impl KeyPair { .to_digest(name, digest_type) .map(|digest| (key_tag, digest)) }) - .map(|(key_tag, digest)| DS::new(key_tag, algorithm, digest_type, digest.to_vec())) + .map(|(key_tag, digest)| DS::new(key_tag, algorithm, digest_type, digest.as_ref().to_owned())) } /// Signs a hash. diff --git a/client/src/rr/dnssec/mod.rs b/client/src/rr/dnssec/mod.rs index 5fc88c75b6..82366d21bd 100644 --- a/client/src/rr/dnssec/mod.rs +++ b/client/src/rr/dnssec/mod.rs @@ -46,3 +46,9 @@ pub use error::DnsSecError; pub use error::DnsSecErrorKind; pub use error::DnsSecChainErr; pub use error::DnsSecResult; + +#[cfg(feature = "openssl")] +pub use openssl::hash::DigestBytes as Digest; + +#[cfg(feature = "ring")] +pub use ring::digest::Digest; diff --git a/client/src/rr/dnssec/nsec3.rs b/client/src/rr/dnssec/nsec3.rs index 4481f4b58f..3149b7b470 100644 --- a/client/src/rr/dnssec/nsec3.rs +++ b/client/src/rr/dnssec/nsec3.rs @@ -13,21 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#[cfg(feature = "openssl")] -use std::io::Write; - -#[cfg(feature = "openssl")] -use openssl::hash; -#[cfg(feature = "openssl")] -use openssl::hash::DigestBytes; use error::*; -#[cfg(feature = "openssl")] -use rr::dnssec::DigestType; -#[cfg(feature = "openssl")] -use rr::Name; -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] use serialize::binary::{BinEncoder, BinSerializable}; +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::Name; +#[cfg(any(feature = "openssl", feature = "ring"))] +use super::{Digest, DigestType}; /// ```text /// RFC 5155 NSEC3 March 2008 @@ -149,8 +142,8 @@ impl Nsec3HashAlgorithm { /// original unexpanded form, including the "*" label (no wildcard /// substitution); /// ``` - #[cfg(feature = "openssl")] - pub fn hash(&self, salt: &[u8], name: &Name, iterations: u16) -> DnsSecResult { + #[cfg(any(feature = "openssl", feature = "ring"))] + pub fn hash(&self, salt: &[u8], name: &Name, iterations: u16) -> DnsSecResult { match *self { // if there ever is more than just SHA1 support, this should be a genericized method Nsec3HashAlgorithm::SHA1 => { @@ -167,25 +160,20 @@ impl Nsec3HashAlgorithm { } /// until there is another supported algorithm, just hardcoded to this. - #[cfg(feature = "openssl")] + #[cfg(any(feature = "openssl", feature = "ring"))] fn sha1_recursive_hash( salt: &[u8], bytes: Vec, iterations: u16, - ) -> DnsSecResult { - let digest_type = try!(DigestType::SHA1.to_openssl_digest()); - hash::Hasher::new(digest_type) - .map_err(|e| e.into()) - .and_then(|mut hasher| { - if iterations > 0 { - let hash = try!(Self::sha1_recursive_hash(salt, bytes, iterations - 1)); - try!(hasher.write_all(&hash)); - } else { - try!(hasher.write_all(&bytes)); - } - try!(hasher.write_all(salt)); - hasher.finish2().map_err(|e| e.into()) - }) + ) -> DnsSecResult { + let digested: Digest; + let to_digest = if iterations > 0 { + digested = try!(Self::sha1_recursive_hash(salt, bytes, iterations - 1)); + digested.as_ref() + } else { + &bytes + }; + DigestType::SHA1.digest_all(&[to_digest, salt]) } } @@ -198,7 +186,7 @@ impl From for u8 { } #[test] -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] fn test_hash() { let name = Name::from_labels(vec!["www", "example", "com"]); @@ -208,6 +196,7 @@ fn test_hash() { Nsec3HashAlgorithm::SHA1 .hash(&salt, &name, 0) .unwrap() + .as_ref() .len(), 20 ); @@ -215,6 +204,7 @@ fn test_hash() { Nsec3HashAlgorithm::SHA1 .hash(&salt, &name, 1) .unwrap() + .as_ref() .len(), 20 ); @@ -222,13 +212,14 @@ fn test_hash() { Nsec3HashAlgorithm::SHA1 .hash(&salt, &name, 3) .unwrap() + .as_ref() .len(), 20 ); } #[test] -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] fn test_known_hashes() { // H(example) = 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom assert_eq!( @@ -298,7 +289,7 @@ fn test_known_hashes() { } #[cfg(test)] -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] fn hash_with_base32(name: &str) -> String { use data_encoding::base32hex; @@ -308,5 +299,5 @@ fn hash_with_base32(name: &str) -> String { let hash = Nsec3HashAlgorithm::SHA1 .hash(&known_salt, &known_name, 12) .unwrap(); - base32hex::encode(&hash).to_lowercase() + base32hex::encode(hash.as_ref()).to_lowercase() } diff --git a/client/src/rr/rdata/dnskey.rs b/client/src/rr/rdata/dnskey.rs index 2404e8485c..4efb5688a2 100644 --- a/client/src/rr/rdata/dnskey.rs +++ b/client/src/rr/rdata/dnskey.rs @@ -18,12 +18,14 @@ use serialize::binary::*; use error::*; -use rr::dnssec::{Algorithm, DigestType}; -use rr::Name; +use rr::dnssec::Algorithm; use rr::record_data::RData; -#[cfg(feature = "openssl")] -use openssl::hash::DigestBytes; +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::Name; + +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::dnssec::{Digest, DigestType}; /// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-2), DNSSEC Resource Records, March 2005 /// @@ -216,8 +218,8 @@ impl DNSKEY { /// /// * `name` - the label of of the DNSKEY record. /// * `digest_type` - the `DigestType` with which to create the message digest. - #[cfg(feature = "openssl")] - pub fn to_digest(&self, name: &Name, digest_type: DigestType) -> DnsSecResult { + #[cfg(any(feature = "openssl", feature = "ring"))] + pub fn to_digest(&self, name: &Name, digest_type: DigestType) -> DnsSecResult { let mut buf: Vec = Vec::new(); { let mut encoder: BinEncoder = BinEncoder::new(&mut buf); @@ -231,12 +233,6 @@ impl DNSKEY { digest_type.hash(&buf).map_err(|e| e.into()) } - - /// This method is only supported when OpenSSL is enabled, `cargo build --features=openssl` - #[cfg(not(feature = "openssl"))] - pub fn to_digest(&self, _: &Name, _: DigestType) -> DnsSecResult> { - panic!("digests require OpenSSL to be enabled, 'cargo build --features=openssl'") - } } impl From for RData { @@ -320,6 +316,7 @@ pub fn emit(encoder: &mut BinEncoder, rdata: &DNSKEY) -> EncodeResult { // fn to_string() #[test] +#[cfg(any(feature = "openssl", feature = "ring"))] pub fn test() { let rdata = DNSKEY::new(true, true, @@ -339,7 +336,6 @@ pub fn test() { assert!(read_rdata.is_ok(), format!("error decoding: {:?}", read_rdata.unwrap_err())); assert_eq!(rdata, read_rdata.unwrap()); - #[cfg(feature = "openssl")] assert!(rdata .to_digest(&Name::parse("www.example.com.", None).unwrap(), DigestType::SHA256) diff --git a/client/src/rr/rdata/ds.rs b/client/src/rr/rdata/ds.rs index b8adaaabcd..630750759c 100644 --- a/client/src/rr/rdata/ds.rs +++ b/client/src/rr/rdata/ds.rs @@ -19,8 +19,11 @@ use ::serialize::binary::*; use ::error::*; use rr::dnssec::{Algorithm, DigestType}; -use rr::Name; + +#[cfg(any(feature = "openssl", feature = "ring"))] use rr::rdata::DNSKEY; +#[cfg(any(feature = "openssl", feature = "ring"))] +use rr::Name; /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-5) /// @@ -170,14 +173,11 @@ impl DS { /// # Return /// /// true if and only if the DNSKEY is covered by the DS record. + #[cfg(any(feature = "openssl", feature = "ring"))] pub fn covers(&self, name: &Name, key: &DNSKEY) -> DnsSecResult { key.to_digest(name, self.digest_type()) .map_err(|e| e.into()) - .map(|hash| if &hash as &[u8] == self.digest() { - return true; - } else { - return false; - }) + .map(|hash| hash.as_ref() == self.digest()) } } @@ -227,7 +227,7 @@ pub fn test() { } #[test] -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] pub fn test_covers() { use rr::rdata::DNSKEY; @@ -237,7 +237,7 @@ pub fn test_covers() { let ds_rdata = DS::new(0, Algorithm::RSASHA256, DigestType::SHA256, - dnskey_rdata.to_digest(&name, DigestType::SHA256).unwrap().to_vec()); + dnskey_rdata.to_digest(&name, DigestType::SHA256).unwrap().as_ref().to_owned()); assert!(ds_rdata.covers(&name, &dnskey_rdata).unwrap()); } diff --git a/client/src/rr/rdata/key.rs b/client/src/rr/rdata/key.rs index 787cf5b54a..bd2d4526c7 100644 --- a/client/src/rr/rdata/key.rs +++ b/client/src/rr/rdata/key.rs @@ -834,7 +834,7 @@ pub fn test() { assert!(read_rdata.is_ok(), format!("error decoding: {:?}", read_rdata.unwrap_err())); assert_eq!(rdata, read_rdata.unwrap()); - // #[cfg(feature = "openssl")] + // #[cfg(any(feature = "openssl", feature = "ring"))] // assert!(rdata // .to_digest(&Name::parse("www.example.com.", None).unwrap(), // DigestType::SHA256) From cada3f8dad6409544f9c97f9d9245a455a1479c2 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 22 Sep 2017 00:04:21 -1000 Subject: [PATCH 2/9] Fix build when openssl and ring features are both enabled. --- client/src/rr/dnssec/digest_type.rs | 4 ++-- client/src/rr/dnssec/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/rr/dnssec/digest_type.rs b/client/src/rr/dnssec/digest_type.rs index 54db89ff31..c6c9185260 100644 --- a/client/src/rr/dnssec/digest_type.rs +++ b/client/src/rr/dnssec/digest_type.rs @@ -94,7 +94,7 @@ impl DigestType { } /// Hash the data - #[cfg(feature = "openssl")] + #[cfg(all(not(feature = "ring"), feature = "openssl"))] pub fn hash(&self, data: &[u8]) -> DnsSecResult { hash::hash2(try!(self.to_openssl_digest()), data).map_err(|e| e.into()) } @@ -113,7 +113,7 @@ impl DigestType { } /// Digest all the data. - #[cfg(feature = "openssl")] + #[cfg(all(not(feature = "ring"), feature = "openssl"))] pub fn digest_all(&self, data: &[&[u8]]) -> DnsSecResult { use std::io::Write; diff --git a/client/src/rr/dnssec/mod.rs b/client/src/rr/dnssec/mod.rs index 82366d21bd..5fe8a1a9b6 100644 --- a/client/src/rr/dnssec/mod.rs +++ b/client/src/rr/dnssec/mod.rs @@ -47,7 +47,7 @@ pub use error::DnsSecErrorKind; pub use error::DnsSecChainErr; pub use error::DnsSecResult; -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] pub use openssl::hash::DigestBytes as Digest; #[cfg(feature = "ring")] From 0ec5aefed19b3600b7022f96ade0d2dbdfe21313 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 22 Sep 2017 00:04:36 -1000 Subject: [PATCH 3/9] Fix error message to mention *ring*. --- client/src/rr/dnssec/digest_type.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/rr/dnssec/digest_type.rs b/client/src/rr/dnssec/digest_type.rs index c6c9185260..c17c22ae6f 100644 --- a/client/src/rr/dnssec/digest_type.rs +++ b/client/src/rr/dnssec/digest_type.rs @@ -109,7 +109,7 @@ impl DigestType { /// This will always error, enable openssl feature at compile time #[cfg(not(any(feature = "openssl", feature = "ring")))] pub fn hash(&self, _: &[u8]) -> DnsSecResult> { - Err(DnsSecErrorKind::Message("openssl feature not enabled").into()) + Err(DnsSecErrorKind::Message("The openssl and ring features are both disabled").into()) } /// Digest all the data. From a03a2dd8287b596072eff1a034dc6c7a10873380 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 21 Sep 2017 23:38:19 -1000 Subject: [PATCH 4/9] Remove unnecessary implementations of Verifier & PublicKey. The code was non-trivially duplicating critical functionality, making improving it difficult. The PublicKey implementation for KeyPair in particular is highly duplicative of the normal signature verification path. Rather than allowing "things that contain public keys" to be used as public keys, instead insist that the public key be extracted from the thing and then used. Note that no non-test code changed, which indicates that some of this was probably test-only code. --- client/src/rr/dnssec/keypair.rs | 68 ++++++--------------------------- client/src/rr/dnssec/signer.rs | 31 +++++---------- 2 files changed, 21 insertions(+), 78 deletions(-) diff --git a/client/src/rr/dnssec/keypair.rs b/client/src/rr/dnssec/keypair.rs index f36749e731..252da381fc 100644 --- a/client/src/rr/dnssec/keypair.rs +++ b/client/src/rr/dnssec/keypair.rs @@ -8,7 +8,7 @@ #[cfg(feature = "openssl")] use openssl::rsa::Rsa as OpenSslRsa; #[cfg(feature = "openssl")] -use openssl::sign::{Signer, Verifier}; +use openssl::sign::Signer; #[cfg(feature = "openssl")] use openssl::pkey::PKey; #[cfg(feature = "openssl")] @@ -26,18 +26,12 @@ use ring::signature::Ed25519KeyPair; use error::*; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::Name; -use rr::dnssec::{Algorithm, PublicKey}; +use rr::dnssec::Algorithm; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::dnssec::DigestType; -#[cfg(feature = "ring")] -use rr::dnssec::public_key::Ed25519; -#[cfg(feature = "openssl")] -use rr::dnssec::public_key::dnssec_ecdsa_signature_to_der; use rr::rdata::{DNSKEY, KEY}; - #[cfg(any(feature = "openssl", feature = "ring"))] use rr::rdata::DS; - use rr::rdata::key::KeyUsage; /// A public and private key pair, the private portion is not required. @@ -464,51 +458,6 @@ impl KeyPair { } } -impl PublicKey for KeyPair { - fn public_bytes(&self) -> &[u8] { - // FIXME: don't actually need access to the public key bytes? - unimplemented!() - } - - #[allow(unused_variables)] - fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> { - match *self { - #[cfg(feature = "openssl")] - KeyPair::RSA(ref pkey) | - KeyPair::EC(ref pkey) => { - let digest_type = try!(DigestType::from(algorithm).to_openssl_digest()); - let mut verifier = Verifier::new(digest_type, &pkey).unwrap(); - try!(verifier.update(message)); - let result = match *self { - KeyPair::RSA(_) => verifier.finish(signature), - KeyPair::EC(_) => { - let signature_asn1 = dnssec_ecdsa_signature_to_der(signature)?; - verifier.finish(&signature_asn1) - }, - #[cfg(feature = "ring")] - _ => unreachable!(), - }; - result - .map_err(|e| e.into()) - .and_then(|b| if b { - Ok(()) - } else { - Err(DnsSecErrorKind::Message("could not verify").into()) - }) - } - #[cfg(feature = "ring")] - KeyPair::ED25519(ref ed_key) => { - let pub_key = Ed25519::from_public_bytes(&ed_key.public_key_bytes())?; - pub_key - .verify(algorithm, message, signature) - .map_err(|e| e.into()) - } - #[cfg(not(any(feature = "openssl", feature = "ring")))] - _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()), - } - } -} - #[cfg(any(feature = "openssl", feature = "ring"))] #[cfg(test)] mod tests { @@ -548,8 +497,8 @@ mod tests { None, algorithm) .unwrap(); - let public_bytes = key.to_public_bytes().unwrap(); - let pk = PublicKeyEnum::from_public_bytes(&public_bytes, algorithm).unwrap(); + let pk = key.to_public_bytes().unwrap(); + let pk = PublicKeyEnum::from_public_bytes(&pk, algorithm).unwrap(); let bytes = b"www.example.com"; let mut sig = key.sign(algorithm, bytes).unwrap(); @@ -570,20 +519,25 @@ mod tests { None, algorithm) .unwrap(); + let pub_key = key.to_public_bytes().unwrap(); + let pub_key = PublicKeyEnum::from_public_bytes(&pub_key, algorithm).unwrap(); + let neg = key_format .decode_key(&key_format.generate_and_encode(algorithm, None).unwrap(), None, algorithm) .unwrap(); + let neg_pub_key = neg.to_public_bytes().unwrap(); + let neg_pub_key = PublicKeyEnum::from_public_bytes(&neg_pub_key, algorithm).unwrap(); let sig = key.sign(algorithm, bytes).unwrap(); - assert!(key.verify(algorithm, bytes, &sig).is_ok(), + assert!(pub_key.verify(algorithm, bytes, &sig).is_ok(), "algorithm: {:?}", algorithm); assert!(key.to_dnskey(algorithm).unwrap().verify(bytes, &sig).is_ok(), "algorithm: {:?} (dnskey)", algorithm); - assert!(!neg.verify(algorithm, bytes, &sig).is_ok(), + assert!(!neg_pub_key.verify(algorithm, bytes, &sig).is_ok(), "algorithm: {:?} (neg)", algorithm); assert!(!neg.to_dnskey(algorithm).unwrap().verify(bytes, &sig).is_ok(), diff --git a/client/src/rr/dnssec/signer.rs b/client/src/rr/dnssec/signer.rs index 3a66a1abf6..37b9849dee 100644 --- a/client/src/rr/dnssec/signer.rs +++ b/client/src/rr/dnssec/signer.rs @@ -25,7 +25,7 @@ use rr::{Name, RData}; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::dnssec::KeyPair; #[cfg(any(feature = "openssl", feature = "ring"))] -use rr::dnssec::{Algorithm, DnsSecResult, hash, PublicKey, PublicKeyEnum, Verifier}; +use rr::dnssec::{Algorithm, DnsSecResult, hash}; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::rdata::{DNSKEY, KEY, SIG}; #[cfg(any(feature = "openssl", feature = "ring"))] @@ -499,21 +499,6 @@ impl Signer { } } -#[cfg(any(feature = "openssl", feature = "ring"))] -impl Verifier for Signer { - fn algorithm(&self) -> Algorithm { - self.algorithm() - } - - fn key<'k>(&'k self) -> DnsSecResult> { - panic!("Signer is cheating by implementing verify() directly to avoid cloning keys") - } - - fn verify(&self, hash: &[u8], signature: &[u8]) -> DnsSecResult<()> { - self.key().verify(self.algorithm(), hash, signature) - } -} - #[cfg(test)] #[cfg(feature = "openssl")] mod tests { @@ -524,7 +509,7 @@ mod tests { use rr::{DNSClass, Name, Record, RecordType}; use rr::rdata::SIG; use rr::rdata::key::KeyUsage; - use rr::dnssec::Verifier; + use rr::dnssec::{PublicKey, PublicKeyEnum, Verifier}; use op::{Message, Query, UpdateMessage}; pub use super::*; @@ -558,14 +543,15 @@ mod tests { let rsa = Rsa::generate(512).unwrap(); let key = KeyPair::from_rsa(rsa).unwrap(); let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap(); - let signer = Signer::sig0(sig0key, key, Name::root()); + let signer = Signer::sig0(sig0key.clone(), key, Name::root()); let pre_sig0 = pre_sig0(&signer, 0, 300); let sig = signer.sign_message(&question, &pre_sig0).unwrap(); println!("sig: {:?}", sig); assert!(!sig.is_empty()); - assert!(signer.verify_message(&question, &sig, &pre_sig0).is_ok()); + + assert!(sig0key.verify_message(&question, &sig, &pre_sig0).is_ok()); // now test that the sig0 record works correctly. assert!(question.sig0().is_empty()); @@ -576,7 +562,7 @@ mod tests { println!("sig after sign: {:?}", sig); if let &RData::SIG(ref sig) = question.sig0()[0].rdata() { - assert!(signer.verify_message(&question, sig.sig(), &sig).is_ok()); + assert!(sig0key.verify_message(&question, sig.sig(), &sig).is_ok()); } } @@ -624,7 +610,10 @@ mod tests { let hash = hash::hash_rrset_with_rrsig(&rrsig, &rrset).unwrap(); let sig = signer.sign(&hash).unwrap(); - assert!(signer.verify(&hash, &sig).is_ok()); + let pub_key = signer.key().to_public_bytes().unwrap(); + let pub_key = PublicKeyEnum::from_public_bytes(&pub_key, Algorithm::RSASHA256).unwrap(); + + assert!(pub_key.verify(Algorithm::RSASHA256, &hash, &sig).is_ok()); } #[test] From d7dbc146c03c17eca47c998998f537e38f4b40b7 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 21 Sep 2017 21:04:44 -1000 Subject: [PATCH 5/9] Convert EC public key formats without heap allocation. The new ECPublicKey type would also be useful for *ring*-based ECDSA verification. --- client/src/rr/dnssec/mod.rs | 2 ++ client/src/rr/dnssec/public_key.rs | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/rr/dnssec/mod.rs b/client/src/rr/dnssec/mod.rs index 5fe8a1a9b6..594bea0733 100644 --- a/client/src/rr/dnssec/mod.rs +++ b/client/src/rr/dnssec/mod.rs @@ -18,6 +18,8 @@ mod algorithm; mod digest_type; +#[cfg(feature = "openssl")] +mod ec_public_key; pub mod hash; #[cfg(any(feature = "openssl", feature = "ring"))] mod key_format; diff --git a/client/src/rr/dnssec/public_key.rs b/client/src/rr/dnssec/public_key.rs index d2f9406f00..9334fe641d 100644 --- a/client/src/rr/dnssec/public_key.rs +++ b/client/src/rr/dnssec/public_key.rs @@ -30,6 +30,7 @@ use error::*; #[cfg(feature = "openssl")] use rr::dnssec::DigestType; use rr::dnssec::Algorithm; +use rr::dnssec::ec_public_key::ECPublicKey; /// PublicKeys implement the ability to ideally be zero copy abstractions over public keys for verifying signed content. /// @@ -189,12 +190,11 @@ impl<'k> Ec<'k> { _ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()), }; // Key needs to be converted to OpenSSL format - let mut k = vec![0x04]; // POINT_CONVERSION_UNCOMPRESSED - k.extend(public_key); + let k = ECPublicKey::from_unprefixed(public_key, algorithm)?; EcGroup::from_curve_name(curve) .and_then(|group| BigNumContext::new().map(|ctx| (group, ctx))) // FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder - .and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, &k, &mut ctx).map(|point| (group, point) )) + .and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, k.as_ref(), &mut ctx).map(|point| (group, point) )) .and_then(|(group, point)| EcKey::from_public_key(&group, &point)) .and_then(|ec_key| PKey::from_ec_key(ec_key) ) .map_err(|e| e.into()) From f3f821a9aa87093b9ae91550cbd9d30af372951f Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 21 Sep 2017 21:57:06 -1000 Subject: [PATCH 6/9] Add ECDSA verification support using *ring*. --- client/src/client/secure_client_handle.rs | 9 +- client/src/rr/dnssec/mod.rs | 2 +- client/src/rr/dnssec/public_key.rs | 106 ++++++++++++++++++---- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/client/src/client/secure_client_handle.rs b/client/src/client/secure_client_handle.rs index f09a0c7f38..1ead51c6f4 100644 --- a/client/src/client/secure_client_handle.rs +++ b/client/src/client/secure_client_handle.rs @@ -124,13 +124,16 @@ where // send along the algorithms which are supported by this client let mut algorithms = SupportedAlgorithms::new(); + #[cfg(feature = "ring")] + { + algorithms.set(Algorithm::ED25519); + } + algorithms.set(Algorithm::ECDSAP256SHA256); + algorithms.set(Algorithm::ECDSAP384SHA384); #[cfg(feature = "openssl")] { algorithms.set(Algorithm::RSASHA256); - algorithms.set(Algorithm::ECDSAP256SHA256); - algorithms.set(Algorithm::ECDSAP384SHA384); } - #[cfg(feature = "ring")] algorithms.set(Algorithm::ED25519); let dau = EdnsOption::DAU(algorithms); let dhu = EdnsOption::DHU(algorithms); diff --git a/client/src/rr/dnssec/mod.rs b/client/src/rr/dnssec/mod.rs index 594bea0733..2f40e8cd91 100644 --- a/client/src/rr/dnssec/mod.rs +++ b/client/src/rr/dnssec/mod.rs @@ -18,7 +18,7 @@ mod algorithm; mod digest_type; -#[cfg(feature = "openssl")] +#[cfg(any(feature = "openssl", feature = "ring"))] mod ec_public_key; pub mod hash; #[cfg(any(feature = "openssl", feature = "ring"))] diff --git a/client/src/rr/dnssec/public_key.rs b/client/src/rr/dnssec/public_key.rs index 9334fe641d..560601cab9 100644 --- a/client/src/rr/dnssec/public_key.rs +++ b/client/src/rr/dnssec/public_key.rs @@ -16,12 +16,16 @@ use openssl::sign::Verifier; #[cfg(feature = "openssl")] use openssl::pkey::PKey; #[cfg(feature = "openssl")] -use openssl::bn::{BigNum, BigNumContext}; -#[cfg(feature = "openssl")] +use openssl::bn::BigNum; +#[cfg(all(not(feature = "ring"), feature = "openssl"))] +use openssl::bn::BigNumContext; +#[cfg(all(not(feature = "ring"), feature = "openssl"))] use openssl::ec::{EcGroup, EcKey, EcPoint}; -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] use openssl::nid; #[cfg(feature = "ring")] +use ring::signature; +#[cfg(feature = "ring")] use ring::signature::{ED25519_PUBLIC_KEY_LEN, EdDSAParameters, VerificationAlgorithm}; #[cfg(feature = "ring")] use untrusted::Input; @@ -30,6 +34,8 @@ use error::*; #[cfg(feature = "openssl")] use rr::dnssec::DigestType; use rr::dnssec::Algorithm; + +#[cfg(any(feature = "openssl", feature = "ring"))] use rr::dnssec::ec_public_key::ECPublicKey; /// PublicKeys implement the ability to ideally be zero copy abstractions over public keys for verifying signed content. @@ -143,13 +149,13 @@ fn verify_with_pkey(pkey: &PKey, } /// Elyptic Curve public key type -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] pub struct Ec<'k> { raw: &'k [u8], pkey: PKey, } -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] impl<'k> Ec<'k> { /// ```text /// RFC 6605 ECDSA for DNSSEC April 2012 @@ -194,7 +200,7 @@ impl<'k> Ec<'k> { EcGroup::from_curve_name(curve) .and_then(|group| BigNumContext::new().map(|ctx| (group, ctx))) // FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder - .and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, k.as_ref(), &mut ctx).map(|point| (group, point) )) + .and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, k.prefixed_bytes(), &mut ctx).map(|point| (group, point) )) .and_then(|(group, point)| EcKey::from_public_key(&group, &point)) .and_then(|ec_key| PKey::from_ec_key(ec_key) ) .map_err(|e| e.into()) @@ -203,7 +209,7 @@ impl<'k> Ec<'k> { } } -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] fn asn1_emit_integer(output: &mut Vec, int: &[u8]) { assert!(int.len() > 0); output.push(0x02); // INTEGER @@ -234,7 +240,7 @@ fn asn1_emit_integer(output: &mut Vec, int: &[u8]) { output.extend(int_output); } /// Convert raw DNSSEC ECDSA signature to ASN.1 DER format -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] pub fn dnssec_ecdsa_signature_to_der(signature: &[u8]) -> DnsSecResult> { if signature.len() == 0 || signature.len() & 1 != 0 || signature.len() > 127 { return Err("invalid signature length".into()); @@ -247,7 +253,7 @@ pub fn dnssec_ecdsa_signature_to_der(signature: &[u8]) -> DnsSecResult> signature_asn1[1] = (signature_asn1.len() - 2) as u8; Ok(signature_asn1) } -#[cfg(feature = "openssl")] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] impl<'k> PublicKey for Ec<'k> { fn public_bytes(&self) -> &[u8] { self.raw @@ -259,6 +265,68 @@ impl<'k> PublicKey for Ec<'k> { } } +/// Elyptic Curve public key type +#[cfg(feature = "ring")] +type Ec = ECPublicKey; + +#[cfg(feature = "ring")] +impl Ec { + /// ```text + /// RFC 6605 ECDSA for DNSSEC April 2012 + /// + /// 4. DNSKEY and RRSIG Resource Records for ECDSA + /// + /// ECDSA public keys consist of a single value, called "Q" in FIPS + /// 186-3. In DNSSEC keys, Q is a simple bit string that represents the + /// uncompressed form of a curve point, "x | y". + /// + /// The ECDSA signature is the combination of two non-negative integers, + /// called "r" and "s" in FIPS 186-3. The two integers, each of which is + /// formatted as a simple octet string, are combined into a single longer + /// octet string for DNSSEC as the concatenation "r | s". (Conversion of + /// the integers to bit strings is described in Section C.2 of FIPS + /// 186-3.) For P-256, each integer MUST be encoded as 32 octets; for + /// P-384, each integer MUST be encoded as 48 octets. + /// + /// The algorithm numbers associated with the DNSKEY and RRSIG resource + /// records are fully defined in the IANA Considerations section. They + /// are: + /// + /// o DNSKEY and RRSIG RRs signifying ECDSA with the P-256 curve and + /// SHA-256 use the algorithm number 13. + /// + /// o DNSKEY and RRSIG RRs signifying ECDSA with the P-384 curve and + /// SHA-384 use the algorithm number 14. + /// + /// Conformant implementations that create records to be put into the DNS + /// MUST implement signing and verification for both of the above + /// algorithms. Conformant DNSSEC verifiers MUST implement verification + /// for both of the above algorithms. + /// ``` + pub fn from_public_bytes(public_key: &[u8], algorithm: Algorithm) -> DnsSecResult { + ECPublicKey::from_unprefixed(public_key, algorithm) + } +} + +#[cfg(feature = "ring")] +impl PublicKey for Ec { + fn public_bytes(&self) -> &[u8] { + self.unprefixed_bytes() + } + + fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> { + // TODO: assert_eq!(algorithm, self.algorithm); once *ring* allows this. + let alg = match algorithm { + Algorithm::ECDSAP256SHA256 => &signature::ECDSA_P256_SHA256_FIXED, + Algorithm::ECDSAP384SHA384 => &signature::ECDSA_P384_SHA384_FIXED, + _ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()), + }; + signature::verify(alg, Input::from(self.prefixed_bytes()), Input::from(message), + Input::from(signature)) + .map_err(|e| e.into()) + } +} + /// Ed25519 Public key #[cfg(feature = "ring")] pub struct Ed25519<'k> { @@ -391,9 +459,12 @@ pub enum PublicKeyEnum<'k> { /// RSA keypair, supported by OpenSSL #[cfg(feature = "openssl")] Rsa(Rsa<'k>), - /// Ellyptic curve keypair, supported by OpenSSL - #[cfg(feature = "openssl")] + /// Elliptic curve keypair + #[cfg(all(not(feature = "ring"), feature = "openssl"))] Ec(Ec<'k>), + /// Elliptic curve keypair + #[cfg(feature = "ring")] + Ec(Ec), /// Ed25519 public key for the Algorithm::ED25519 #[cfg(feature = "ring")] Ed25519(Ed25519<'k>), @@ -407,11 +478,10 @@ impl<'k> PublicKeyEnum<'k> { #[allow(unused_variables)] pub fn from_public_bytes(public_key: &'k [u8], algorithm: Algorithm) -> DnsSecResult { match algorithm { - #[cfg(feature = "openssl")] + #[cfg(any(feature = "openssl", feature = "ring"))] Algorithm::ECDSAP256SHA256 | - Algorithm::ECDSAP384SHA384 => { - Ok(PublicKeyEnum::Ec(Ec::from_public_bytes(public_key, algorithm)?)) - } + Algorithm::ECDSAP384SHA384 => + Ok(PublicKeyEnum::Ec(Ec::from_public_bytes(public_key, algorithm)?)), #[cfg(feature = "ring")] Algorithm::ED25519 => { Ok(PublicKeyEnum::Ed25519(Ed25519::from_public_bytes(public_key)?)) @@ -430,7 +500,7 @@ impl<'k> PublicKeyEnum<'k> { impl<'k> PublicKey for PublicKeyEnum<'k> { fn public_bytes(&self) -> &[u8] { match *self { - #[cfg(feature = "openssl")] + #[cfg(any(feature = "openssl", feature="ring"))] PublicKeyEnum::Ec(ref ec) => ec.public_bytes(), #[cfg(feature = "ring")] PublicKeyEnum::Ed25519(ref ed) => ed.public_bytes(), @@ -444,7 +514,7 @@ impl<'k> PublicKey for PublicKeyEnum<'k> { #[allow(unused_variables)] fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> { match *self { - #[cfg(feature = "openssl")] + #[cfg(any(feature = "openssl", feature="ring"))] PublicKeyEnum::Ec(ref ec) => ec.verify(algorithm, message, signature), #[cfg(feature = "ring")] PublicKeyEnum::Ed25519(ref ed) => ed.verify(algorithm, message, signature), @@ -456,7 +526,7 @@ impl<'k> PublicKey for PublicKeyEnum<'k> { } } -#[cfg(any(feature = "openssl", feature = "ring"))] +#[cfg(all(not(feature = "ring"), feature = "openssl"))] #[cfg(test)] mod tests { #[cfg(feature = "openssl")] From f7366b808b507d5ab12250ae3ba8a5cc52b1d1b2 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 22 Sep 2017 00:32:08 -1000 Subject: [PATCH 7/9] Add missing ec_public_key.rs file. --- client/src/rr/dnssec/ec_public_key.rs | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100755 client/src/rr/dnssec/ec_public_key.rs diff --git a/client/src/rr/dnssec/ec_public_key.rs b/client/src/rr/dnssec/ec_public_key.rs new file mode 100755 index 0000000000..d4ba034faf --- /dev/null +++ b/client/src/rr/dnssec/ec_public_key.rs @@ -0,0 +1,47 @@ +// Copyright 2017 Brian Smith +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use error::*; +use super::Algorithm; + +pub struct ECPublicKey { + buf: [u8; MAX_LEN], + len: usize, +} + +// The length of the longest supported EC public key (P-384). +const MAX_LEN: usize = 1 + (2 * 48); + +impl ECPublicKey { + // DNSSEC encodes uncompressed EC public keys without the standard 0x04 + // prefix that indicates they are uncompressed, but crypto libraries + // require that prefix. + pub fn from_unprefixed(without_prefix: &[u8], algorithm: Algorithm) + -> DnsSecResult { + let field_len = match algorithm { + Algorithm::ECDSAP256SHA256 => 32, + Algorithm::ECDSAP384SHA384 => 48, + _ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()), + }; + let len = 1 + (2 * field_len); + if len - 1 != without_prefix.len() { + return Err("EC public key is the wrong length".into()); + } + let mut buf = [0x04u8; MAX_LEN]; + buf[1..len].copy_from_slice(without_prefix); + Ok( ECPublicKey { buf, len }) + } + + pub fn prefixed_bytes(&self) -> &[u8] { + &self.buf[..self.len] + } + + #[cfg(feature = "ring")] + pub fn unprefixed_bytes(&self) -> &[u8] { + &self.buf[1..self.len] + } +} From d1a5fb6c3e98ad7b729685cfa3bda7c4ff2085f2 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 22 Sep 2017 11:58:04 -1000 Subject: [PATCH 8/9] Clarify what values are digests and what values are to-be-digested/signed. During a previous refactoring the digesting of message contents was moved closer to the place where the signing is done, to accomodate Ed25519 and crypto APIs that don't take digests as input to signining functions. However, functions that used to return digests kept their names with a "_hash" suffix or "hash_" prefix, which is confusing. Clarify that by renaming all such functions. Further, introduce a new TBS wrapper type that further clarifies the intent of the code. "TBS" is a common shorthand in crypto code standing for "to be signed" data, e.g. tbsCertificate in X.509. --- client/src/rr/dnssec/keypair.rs | 30 ++++--- client/src/rr/dnssec/mod.rs | 2 +- client/src/rr/dnssec/signer.rs | 16 ++-- client/src/rr/dnssec/{hash.rs => tbs.rs} | 108 +++++++++++++---------- client/src/rr/dnssec/verifier.rs | 10 +-- server/src/authority/authority.rs | 33 +++---- 6 files changed, 110 insertions(+), 89 deletions(-) rename client/src/rr/dnssec/{hash.rs => tbs.rs} (85%) diff --git a/client/src/rr/dnssec/keypair.rs b/client/src/rr/dnssec/keypair.rs index 252da381fc..7e897ef72d 100644 --- a/client/src/rr/dnssec/keypair.rs +++ b/client/src/rr/dnssec/keypair.rs @@ -33,6 +33,7 @@ use rr::rdata::{DNSKEY, KEY}; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::rdata::DS; use rr::rdata::key::KeyUsage; +use rr::dnssec::tbs::TBS; /// A public and private key pair, the private portion is not required. /// @@ -301,20 +302,20 @@ impl KeyPair { /// /// # Arguments /// - /// * `message` - the message bytes to be signed, see `hash_rrset`. + /// * `message` - the message bytes to be signed, see `rrset_tbs`. /// /// # Return value /// /// The signature, ready to be stored in an `RData::RRSIG`. #[allow(unused)] - pub fn sign(&self, algorithm: Algorithm, message: &[u8]) -> DnsSecResult> { + pub fn sign(&self, algorithm: Algorithm, tbs: &TBS) -> DnsSecResult> { match *self { #[cfg(feature = "openssl")] KeyPair::RSA(ref pkey) | KeyPair::EC(ref pkey) => { let digest_type = try!(DigestType::from(algorithm).to_openssl_digest()); let mut signer = Signer::new(digest_type, &pkey).unwrap(); - try!(signer.update(&message)); + try!(signer.update(tbs.as_ref())); signer.finish().map_err(|e| e.into()) .and_then(|bytes| { if let KeyPair::RSA(_) = *self { @@ -382,7 +383,7 @@ impl KeyPair { }) } #[cfg(feature = "ring")] - KeyPair::ED25519(ref ed_key) => Ok(ed_key.sign(message).as_ref().to_vec()), + KeyPair::ED25519(ref ed_key) => Ok(ed_key.sign(tbs.as_ref()).as_ref().to_vec()), #[cfg(not(any(feature = "openssl", feature = "ring")))] _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()), } @@ -462,6 +463,7 @@ impl KeyPair { #[cfg(test)] mod tests { use rr::dnssec::*; + use rr::dnssec::tbs::TBS; #[cfg(feature = "openssl")] #[test] @@ -500,18 +502,18 @@ mod tests { let pk = key.to_public_bytes().unwrap(); let pk = PublicKeyEnum::from_public_bytes(&pk, algorithm).unwrap(); - let bytes = b"www.example.com"; - let mut sig = key.sign(algorithm, bytes).unwrap(); - assert!(pk.verify(algorithm, bytes, &sig).is_ok(), + let tbs = TBS::from(&b"www.example.com"[..]); + let mut sig = key.sign(algorithm, &tbs).unwrap(); + assert!(pk.verify(algorithm, tbs.as_ref(), &sig).is_ok(), "algorithm: {:?} (public key)", algorithm); sig[10] = !sig[10]; - assert!(!pk.verify(algorithm, bytes, &sig).is_ok(), + assert!(!pk.verify(algorithm, tbs.as_ref(), &sig).is_ok(), "algorithm: {:?} (public key, neg)", algorithm); } fn hash_test(algorithm: Algorithm, key_format: KeyFormat) { - let bytes = b"www.example.com"; + let tbs = TBS::from(&b"www.example.com"[..]); // TODO: convert to stored keys... let key = key_format @@ -530,17 +532,17 @@ mod tests { let neg_pub_key = neg.to_public_bytes().unwrap(); let neg_pub_key = PublicKeyEnum::from_public_bytes(&neg_pub_key, algorithm).unwrap(); - let sig = key.sign(algorithm, bytes).unwrap(); - assert!(pub_key.verify(algorithm, bytes, &sig).is_ok(), + let sig = key.sign(algorithm, &tbs).unwrap(); + assert!(pub_key.verify(algorithm, tbs.as_ref(), &sig).is_ok(), "algorithm: {:?}", algorithm); - assert!(key.to_dnskey(algorithm).unwrap().verify(bytes, &sig).is_ok(), + assert!(key.to_dnskey(algorithm).unwrap().verify(tbs.as_ref(), &sig).is_ok(), "algorithm: {:?} (dnskey)", algorithm); - assert!(!neg_pub_key.verify(algorithm, bytes, &sig).is_ok(), + assert!(!neg_pub_key.verify(algorithm, tbs.as_ref(), &sig).is_ok(), "algorithm: {:?} (neg)", algorithm); - assert!(!neg.to_dnskey(algorithm).unwrap().verify(bytes, &sig).is_ok(), + assert!(!neg.to_dnskey(algorithm).unwrap().verify(tbs.as_ref(), &sig).is_ok(), "algorithm: {:?} (dnskey, neg)", algorithm); } diff --git a/client/src/rr/dnssec/mod.rs b/client/src/rr/dnssec/mod.rs index 2f40e8cd91..0412c97924 100644 --- a/client/src/rr/dnssec/mod.rs +++ b/client/src/rr/dnssec/mod.rs @@ -20,7 +20,7 @@ mod algorithm; mod digest_type; #[cfg(any(feature = "openssl", feature = "ring"))] mod ec_public_key; -pub mod hash; +pub mod tbs; #[cfg(any(feature = "openssl", feature = "ring"))] mod key_format; mod keypair; diff --git a/client/src/rr/dnssec/signer.rs b/client/src/rr/dnssec/signer.rs index 37b9849dee..891c3c36a4 100644 --- a/client/src/rr/dnssec/signer.rs +++ b/client/src/rr/dnssec/signer.rs @@ -25,7 +25,7 @@ use rr::{Name, RData}; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::dnssec::KeyPair; #[cfg(any(feature = "openssl", feature = "ring"))] -use rr::dnssec::{Algorithm, DnsSecResult, hash}; +use rr::dnssec::{Algorithm, DnsSecResult, tbs}; #[cfg(any(feature = "openssl", feature = "ring"))] use rr::rdata::{DNSKEY, KEY, SIG}; #[cfg(any(feature = "openssl", feature = "ring"))] @@ -478,7 +478,7 @@ impl Signer { /// /// --- pub fn sign_message(&self, message: &Message, pre_sig0: &SIG) -> DnsSecResult> { - hash::hash_message(message, pre_sig0).and_then(|hash| self.sign(&hash)) + tbs::message_tbs(message, pre_sig0).and_then(|tbs| self.sign(&tbs)) } /// Signs a hash. @@ -487,14 +487,14 @@ impl Signer { /// /// # Arguments /// - /// * `hash` - the hashed resource record set, see `hash_rrset`. + /// * `hash` - the hashed resource record set, see `rrset_tbs`. /// /// # Return value /// /// The signature, ready to be stored in an `RData::RRSIG`. - pub fn sign(&self, hash: &[u8]) -> DnsSecResult> { + pub fn sign(&self, tbs: &tbs::TBS) -> DnsSecResult> { self.key - .sign(self.algorithm, &hash) + .sign(self.algorithm, tbs) .map_err(|e| e.into()) } } @@ -607,13 +607,13 @@ mod tests { .set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap())) .clone()]; - let hash = hash::hash_rrset_with_rrsig(&rrsig, &rrset).unwrap(); - let sig = signer.sign(&hash).unwrap(); + let tbs = tbs::rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap(); + let sig = signer.sign(&tbs).unwrap(); let pub_key = signer.key().to_public_bytes().unwrap(); let pub_key = PublicKeyEnum::from_public_bytes(&pub_key, Algorithm::RSASHA256).unwrap(); - assert!(pub_key.verify(Algorithm::RSASHA256, &hash, &sig).is_ok()); + assert!(pub_key.verify(Algorithm::RSASHA256, tbs.as_ref(), &sig).is_ok()); } #[test] diff --git a/client/src/rr/dnssec/hash.rs b/client/src/rr/dnssec/tbs.rs similarity index 85% rename from client/src/rr/dnssec/hash.rs rename to client/src/rr/dnssec/tbs.rs index 16e8dcae53..f9ce2ae636 100644 --- a/client/src/rr/dnssec/hash.rs +++ b/client/src/rr/dnssec/tbs.rs @@ -6,8 +6,24 @@ use rr::dnssec::{Algorithm, DnsSecErrorKind, DnsSecResult}; use rr::rdata::{sig, SIG}; use serialize::binary::{BinEncoder, BinSerializable, EncodeMode}; -/// Hashes a Message for signing -pub fn hash_message(message: &Message, pre_sig0: &SIG) -> DnsSecResult> { +/// Data To Be Signed. +pub struct TBS(Vec); + +#[cfg(test)] +impl<'a> From<&'a [u8]> for TBS { + fn from(slice: &'a [u8]) -> Self { + TBS(slice.to_owned()) + } +} + +impl AsRef<[u8]> for TBS { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +/// Returns the to-be-signed serialization of the given message. +pub fn message_tbs(message: &Message, pre_sig0: &SIG) -> DnsSecResult { // TODO: should perform the serialization and sign block by block to reduce the max memory // usage, though at 4k max, this is probably unnecessary... For AXFR and large zones, it's // more important @@ -35,10 +51,10 @@ pub fn hash_message(message: &Message, pre_sig0: &SIG) -> DnsSecResult> buf.append(&mut buf2); - Ok(buf) + Ok(TBS(buf)) } -/// Computes the hash of the given record set +/// Returns the to-be-signed serialization of the given record set. /// /// # Arguments /// @@ -56,18 +72,18 @@ pub fn hash_message(message: &Message, pre_sig0: &SIG) -> DnsSecResult> /// # Returns /// /// the binary hash of the specified RRSet and associated information -pub fn hash_rrset(name: &Name, - dns_class: DNSClass, - num_labels: u8, - type_covered: RecordType, - algorithm: Algorithm, - original_ttl: u32, - sig_expiration: u32, - sig_inception: u32, - key_tag: u16, - signer_name: &Name, - records: &[Record]) - -> DnsSecResult> { +pub fn rrset_tbs(name: &Name, + dns_class: DNSClass, + num_labels: u8, + type_covered: RecordType, + algorithm: Algorithm, + original_ttl: u32, + sig_expiration: u32, + sig_inception: u32, + key_tag: u16, + signer_name: &Name, + records: &[Record]) + -> DnsSecResult { // TODO: change this to a BTreeSet so that it's preordered, no sort necessary let mut rrset: Vec<&Record> = Vec::new(); @@ -146,12 +162,11 @@ pub fn hash_rrset(name: &Name, } } - // TODO: This used to return the hash, now it's a hashable record type? - // DigestType::from(self.algorithm).hash(&buf) - Ok(buf) + Ok(TBS(buf)) } -/// hashes the RRSet with information provided from the RRSig record +/// Returns the to-be-signed serialization of the given record set using the information +/// provided from the RRSIG record. /// /// # Arguments /// @@ -161,16 +176,17 @@ pub fn hash_rrset(name: &Name, /// # Return /// /// binary hash of the RRSet with the information from the RRSIG record -pub fn hash_rrset_with_rrsig(rrsig: &Record, records: &[Record]) -> DnsSecResult> { +pub fn rrset_tbs_with_rrsig(rrsig: &Record, records: &[Record]) -> DnsSecResult { if let &RData::SIG(ref sig) = rrsig.rdata() { - hash_rrset_with_sig(rrsig.name(), rrsig.dns_class(), sig, records) + rrset_tbs_with_sig(rrsig.name(), rrsig.dns_class(), sig, records) } else { return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}", rrsig.name())) .into()); } } -/// hashes the RRSet with information provided from the RRSig record +/// Returns the to-be-signed serialization of the given record set using the information +/// provided from the SIG record. /// /// # Arguments /// @@ -182,22 +198,22 @@ pub fn hash_rrset_with_rrsig(rrsig: &Record, records: &[Record]) -> DnsSecResult /// # Return /// /// binary hash of the RRSet with the information from the RRSIG record -pub fn hash_rrset_with_sig(name: &Name, - dns_class: DNSClass, - sig: &SIG, - records: &[Record]) - -> DnsSecResult> { - hash_rrset(name, - dns_class, - sig.num_labels(), - sig.type_covered(), - sig.algorithm(), - sig.original_ttl(), - sig.sig_expiration(), - sig.sig_inception(), - sig.key_tag(), - sig.signer_name(), - records) +pub fn rrset_tbs_with_sig(name: &Name, + dns_class: DNSClass, + sig: &SIG, + records: &[Record]) + -> DnsSecResult { + rrset_tbs(name, + dns_class, + sig.num_labels(), + sig.type_covered(), + sig.algorithm(), + sig.original_ttl(), + sig.sig_expiration(), + sig.sig_inception(), + sig.key_tag(), + sig.signer_name(), + records) } @@ -278,7 +294,7 @@ mod tests { pub use super::*; #[test] - fn test_hash_rrset() { + fn test_rrset_tbs() { let rsa = Rsa::generate(512).unwrap(); let key = KeyPair::from_rsa(rsa).unwrap(); let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap(); @@ -316,8 +332,8 @@ mod tests { .set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap())) .clone()]; - let hash = hash_rrset_with_rrsig(&rrsig, &rrset).unwrap(); - assert!(!hash.is_empty()); + let tbs = rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap(); + assert!(!tbs.0.is_empty()); let rrset = vec![Record::new() @@ -357,8 +373,8 @@ mod tests { .set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap())) .clone()]; - let filtered_hash = hash_rrset_with_rrsig(&rrsig, &rrset).unwrap(); - assert!(!filtered_hash.is_empty()); - assert_eq!(hash, filtered_hash); + let filtered_tbs = rrset_tbs_with_rrsig(&rrsig, &rrset).unwrap(); + assert!(!filtered_tbs.0.is_empty()); + assert_eq!(tbs.as_ref(), filtered_tbs.as_ref()); } -} \ No newline at end of file +} diff --git a/client/src/rr/dnssec/verifier.rs b/client/src/rr/dnssec/verifier.rs index 87c1572550..1fad4b5118 100644 --- a/client/src/rr/dnssec/verifier.rs +++ b/client/src/rr/dnssec/verifier.rs @@ -3,7 +3,7 @@ use op::Message; use rr::{DNSClass, Name, Record}; use rr::dnssec::{Algorithm, DnsSecResult}; -use rr::dnssec::{hash, PublicKey, PublicKeyEnum}; +use rr::dnssec::{tbs, PublicKey, PublicKeyEnum}; use rr::rdata::{DNSKEY, KEY, SIG}; /// Types which are able to verify DNS based signatures @@ -18,7 +18,7 @@ pub trait Verifier { /// /// # Arguments /// - /// * `hash` - the hash to be validated, see `hash_rrset` + /// * `hash` - the hash to be validated, see `rrset_tbs` /// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG` /// for example. /// @@ -41,7 +41,7 @@ pub trait Verifier { /// /// `true` if the message could be validated against the signature, `false` otherwise fn verify_message(&self, message: &Message, signature: &[u8], sig0: &SIG) -> DnsSecResult<()> { - hash::hash_message(message, sig0).and_then(|hash| self.verify(&hash, signature)) + tbs::message_tbs(message, sig0).and_then(|tbs| self.verify(tbs.as_ref(), signature)) } /// Verifies an RRSig with the associated key, e.g. DNSKEY @@ -58,8 +58,8 @@ pub trait Verifier { sig: &SIG, records: &[Record]) -> DnsSecResult<()> { - let rrset_hash = hash::hash_rrset_with_sig(name, dns_class, sig, records)?; - self.verify(&rrset_hash, sig.sig()) + let rrset_tbs = tbs::rrset_tbs_with_sig(name, dns_class, sig, records)?; + self.verify(rrset_tbs.as_ref(), sig.sig()) } } diff --git a/server/src/authority/authority.rs b/server/src/authority/authority.rs index a63a8d7b80..be6edd74c7 100644 --- a/server/src/authority/authority.rs +++ b/server/src/authority/authority.rs @@ -23,7 +23,7 @@ use trust_dns::error::*; use trust_dns::op::{Message, UpdateMessage, ResponseCode, Query}; use trust_dns::rr::{DNSClass, Name, RData, Record, RecordType, RrKey, RecordSet}; use trust_dns::rr::rdata::{NSEC, SIG}; -use trust_dns::rr::dnssec::{hash, Signer, SupportedAlgorithms, Verifier}; +use trust_dns::rr::dnssec::{tbs, Signer, SupportedAlgorithms, Verifier}; use authority::{Journal, UpdateResult, ZoneType}; use error::{PersistenceErrorKind, PersistenceResult}; @@ -1146,7 +1146,7 @@ impl Authority { for signer in self.secure_keys.iter() { let expiration = inception + signer.sig_duration(); - let hash = hash::hash_rrset( + let tbs = tbs::rrset_tbs( rr_set.name(), self.class, rr_set.name().num_labels(), @@ -1167,19 +1167,22 @@ impl Authority { ); // TODO, maybe chain these with some ETL operations instead? - if hash.is_err() { - error!("could not hash rrset to sign: {}", hash.unwrap_err()); - continue; - } - let hash = hash.unwrap(); - - let signature = signer.sign(&hash); - - if signature.is_err() { - error!("could not sign hash of rrset: {}", signature.unwrap_err()); - continue; - } - let signature = signature.unwrap(); + let tbs = match tbs { + Ok(tbs) => tbs, + Err(err) => { + error!("could not serialize rrset to sign: {}", err); + continue; + } + }; + + let signature = signer.sign(&tbs); + let signature = match signature { + Ok(signature) => signature, + Err(err) => { + error!("could not sign rrset: {}", err); + continue; + } + }; let mut rrsig = rrsig_temp.clone(); rrsig.set_rdata(RData::SIG(SIG::new( From 7a7dbcaa1d37b4a0106529560f29d6452225d429 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 22 Sep 2017 10:09:05 -1000 Subject: [PATCH 9/9] Split dnskey_to_pem.rs out into its own util crate. Move the utility programs into their own crate that complements the main TRust-DNS crate. This will allow their dependencies to evolve separately from the main crate. In particular, this is a step towards getting the fully-DNSSEC-capable client working using only *ring*, because it moves OpenSSL-specific code out of the client. --- Cargo.lock | 74 +++++++++++++++++++-------- Cargo.toml | 2 +- client/Cargo.toml | 7 --- util/Cargo.toml | 43 ++++++++++++++++ {client => util}/src/dnskey_to_pem.rs | 0 5 files changed, 98 insertions(+), 28 deletions(-) create mode 100755 util/Cargo.toml rename {client => util}/src/dnskey_to_pem.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 779fb52f83..ba18d91345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,26 +1,13 @@ [root] -name = "trust-dns-server" -version = "0.10.8" +name = "trust-dns-util" +version = "0.1.0" dependencies = [ - "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-openssl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns 0.11.4", - "trust-dns-native-tls 0.1.4", + "trust-dns 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1149,7 +1136,8 @@ dependencies = [ [[package]] name = "trust-dns" -version = "0.11.4" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1161,6 +1149,26 @@ dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-openssl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns" +version = "0.11.4" +dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1239,6 +1247,31 @@ dependencies = [ "trust-dns 0.11.4", ] +[[package]] +name = "trust-dns-server" +version = "0.10.8" +dependencies = [ + "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-openssl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns 0.11.4", + "trust-dns-native-tls 0.1.4", +] + [[package]] name = "unicode-width" version = "0.1.4" @@ -1470,6 +1503,7 @@ dependencies = [ "checksum tokio-rustls 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9263e472d976e4345e50c6cce4cfe6b17c71593ea593cce1df26f1efd36debb" "checksum tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d88e411cac1c87e405e4090be004493c5d8072a370661033b1a64ea205ec2e13" "checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796" +"checksum trust-dns 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ea4c98414b0e828b5917c93a3e1fc41605d2ff430033ef6cb3b4905688cbeae" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" diff --git a/Cargo.toml b/Cargo.toml index 0fe558aece..0965843b26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["client", "compatibility-tests", "integration-tests", "native-tls", "resolver", "rustls", "server"] +members = ["client", "compatibility-tests", "integration-tests", "native-tls", "resolver", "rustls", "server", "util"] [replace] #"native-tls:0.1.1" = { path = "../rust-native-tls" } diff --git a/client/Cargo.toml b/client/Cargo.toml index 7a8ce5e40c..d9b797cea9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -50,16 +50,9 @@ tls = ["openssl", "tokio-openssl"] name = "trust_dns" path = "src/lib.rs" -[[bin]] -name = "dnskey-to-pem" -required-features = ["openssl"] -path = "src/dnskey_to_pem.rs" - [dependencies] chrono = "^0.4" -clap = "^2.23.3" data-encoding = "^1.2.0" -env_logger = "0.4.2" error-chain = "0.1.12" futures = "^0.1.6" lazy_static = "^0.2.1" diff --git a/util/Cargo.toml b/util/Cargo.toml new file mode 100755 index 0000000000..f72072672d --- /dev/null +++ b/util/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "trust-dns-util" +version = "0.1.0" +authors = ["Benjamin Fry "] + +# A short blurb about the package. This is not rendered in any format when +# uploaded to crates.io (aka this is not markdown) +description = """ +Utilities that complement TRust-DNS. +""" + +# These URLs point to more information about the repository +documentation = "https://docs.rs/trust-dns" +homepage = "http://www.trust-dns.org/index.html" +repository = "https://github.com/bluejekyll/trust-dns" + +# This is a small list of keywords used to categorize and search for this +# package. +keywords = ["DNS", "BIND", "dig", "named", "dnssec"] +categories = ["network-programming"] + +# This is a string description of the license for this package. Currently +# crates.io will validate the license provided against a whitelist of known +# license identifiers from http://spdx.org/licenses/. Multiple licenses can +# be separated with a `/` +license = "MIT/Apache-2.0" + +[badges] +travis-ci = { repository = "bluejekyll/trust-dns" } +appveyor = { repository = "bluejekyll/trust-dns", branch = "master", service = "github" } +coveralls = { repository = "bluejekyll/trust-dns", branch = "master", service = "github" } + +[[bin]] +name = "dnskey-to-pem" +path = "src/dnskey_to_pem.rs" + +[dependencies] +clap = "^2.23.3" +data-encoding = "^1.2.0" +trust-dns = "0.11.3" +env_logger = "0.4.2" +log = "^0.3.5" +openssl = { version = "^0.9.8", features = ["v102", "v110"] } diff --git a/client/src/dnskey_to_pem.rs b/util/src/dnskey_to_pem.rs similarity index 100% rename from client/src/dnskey_to_pem.rs rename to util/src/dnskey_to_pem.rs