Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable more digest-related functionality for *ring*. #194

Merged
merged 4 commits into from
Sep 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 63 additions & 11 deletions client/src/rr/dnssec/digest_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -62,29 +66,77 @@ impl DigestType {

/// The OpenSSL counterpart for the digest
#[cfg(feature = "openssl")]
pub fn to_openssl_digest(&self) -> DnsSecResult<MessageDigest> {
pub fn to_openssl_digest(&self) -> DnsSecResult<hash::MessageDigest> {
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())
}
}
}

/// 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<DigestBytes> {
#[cfg(all(not(feature = "ring"), feature = "openssl"))]
pub fn hash(&self, data: &[u8]) -> DnsSecResult<Digest> {
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<Digest> {
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<Vec<u8>> {
Err(DnsSecErrorKind::Message("openssl feature not enabled").into())
Err(DnsSecErrorKind::Message("The openssl and ring features are both disabled").into())
}

/// Digest all the data.
#[cfg(all(not(feature = "ring"), feature = "openssl"))]
pub fn digest_all(&self, data: &[&[u8]]) -> DnsSecResult<Digest> {
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<Digest> {
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())
}
}

Expand Down
14 changes: 11 additions & 3 deletions client/src/rr/dnssec/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions client/src/rr/dnssec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ pub use error::DnsSecError;
pub use error::DnsSecErrorKind;
pub use error::DnsSecChainErr;
pub use error::DnsSecResult;

#[cfg(all(not(feature = "ring"), feature = "openssl"))]
pub use openssl::hash::DigestBytes as Digest;

#[cfg(feature = "ring")]
pub use ring::digest::Digest;
57 changes: 24 additions & 33 deletions client/src/rr/dnssec/nsec3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<DigestBytes> {
#[cfg(any(feature = "openssl", feature = "ring"))]
pub fn hash(&self, salt: &[u8], name: &Name, iterations: u16) -> DnsSecResult<Digest> {
match *self {
// if there ever is more than just SHA1 support, this should be a genericized method
Nsec3HashAlgorithm::SHA1 => {
Expand All @@ -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<u8>,
iterations: u16,
) -> DnsSecResult<DigestBytes> {
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<Digest> {
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])
}
}

Expand All @@ -198,7 +186,7 @@ impl From<Nsec3HashAlgorithm> for u8 {
}

#[test]
#[cfg(feature = "openssl")]
#[cfg(any(feature = "openssl", feature = "ring"))]
fn test_hash() {

let name = Name::from_labels(vec!["www", "example", "com"]);
Expand All @@ -208,27 +196,30 @@ fn test_hash() {
Nsec3HashAlgorithm::SHA1
.hash(&salt, &name, 0)
.unwrap()
.as_ref()
.len(),
20
);
assert_eq!(
Nsec3HashAlgorithm::SHA1
.hash(&salt, &name, 1)
.unwrap()
.as_ref()
.len(),
20
);
assert_eq!(
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!(
Expand Down Expand Up @@ -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;

Expand All @@ -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()
}
22 changes: 9 additions & 13 deletions client/src/rr/rdata/dnskey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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<DigestBytes> {
#[cfg(any(feature = "openssl", feature = "ring"))]
pub fn to_digest(&self, name: &Name, digest_type: DigestType) -> DnsSecResult<Digest> {
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
Expand All @@ -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<Vec<u8>> {
panic!("digests require OpenSSL to be enabled, 'cargo build --features=openssl'")
}
}

impl From<DNSKEY> for RData {
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down
16 changes: 8 additions & 8 deletions client/src/rr/rdata/ds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
///
Expand Down Expand Up @@ -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<bool> {
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())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, I love it when I forget to just return the result of ==. This is definitely cleaner.

}
}

Expand Down Expand Up @@ -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;

Expand All @@ -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());
}