[features]
default = ["validation"]
std = []
-validation = ["ring", "hex_lit"]
+validation = ["bitcoin_hashes", "ring", "hex_lit"]
tokio = ["tokio_crate/net", "tokio_crate/io-util", "std"]
build_server = ["tokio", "tokio_crate/rt-multi-thread", "tokio_crate/macros"]
[dependencies]
ring = { version = "0.17", default-features = false, features = ["alloc"], optional = true }
+bitcoin_hashes = { version = "0.13", default-features = false, optional = true }
hex_lit = { version = "0.1", default-features = false, features = ["rust_v_1_46"], optional = true }
tokio_crate = { package = "tokio", version = "1.0", default-features = false, optional = true }
--- /dev/null
+//! Simple wrapper around various hash options to provide a single enum which can calculate
+//! different hashes.
+
+use bitcoin_hashes::Hash;
+use bitcoin_hashes::HashEngine as _;
+use bitcoin_hashes::sha1::Hash as Sha1;
+use bitcoin_hashes::sha256::Hash as Sha256;
+use bitcoin_hashes::sha512::Hash as Sha512;
+
+pub(crate) enum Hasher {
+ Sha1(<Sha1 as Hash>::Engine),
+ Sha256(<Sha256 as Hash>::Engine),
+ #[allow(unused)]
+ Sha512(<Sha512 as Hash>::Engine),
+}
+
+pub(crate) enum HashResult {
+ Sha1(Sha1),
+ Sha256(Sha256),
+ Sha512(Sha512),
+}
+
+impl AsRef<[u8]> for HashResult {
+ fn as_ref(&self) -> &[u8] {
+ match self {
+ HashResult::Sha1(hash) => hash.as_ref(),
+ HashResult::Sha256(hash) => hash.as_ref(),
+ HashResult::Sha512(hash) => hash.as_ref(),
+ }
+ }
+}
+
+impl Hasher {
+ pub(crate) fn sha1() -> Hasher { Hasher::Sha1(Sha1::engine()) }
+ pub(crate) fn sha256() -> Hasher { Hasher::Sha256(Sha256::engine()) }
+ #[allow(unused)]
+ pub(crate) fn sha512() -> Hasher { Hasher::Sha512(Sha512::engine()) }
+
+ pub(crate) fn update(&mut self, buf: &[u8]) {
+ match self {
+ Hasher::Sha1(hasher) => hasher.input(buf),
+ Hasher::Sha256(hasher) => hasher.input(buf),
+ Hasher::Sha512(hasher) => hasher.input(buf),
+ }
+ }
+
+ pub(crate) fn finish(self) -> HashResult {
+ match self {
+ Hasher::Sha1(hasher) => HashResult::Sha1(Sha1::from_engine(hasher)),
+ Hasher::Sha256(hasher) => HashResult::Sha256(Sha256::from_engine(hasher)),
+ Hasher::Sha512(hasher) => HashResult::Sha512(Sha512::from_engine(hasher)),
+ }
+ }
+}
+
--- /dev/null
+//! Implementations of cryptographic verification
+//!
+//! Sadly, the choices for cryptographic verification in Rust are somewhat limited. For us (RSA and
+//! secp256r1/secp384r1) there's really only `ring` and `RustCrypto`.
+//!
+//! While `ring` is great, it struggles with platform support and has a fairly involved dependency
+//! tree due to its reliance on C backends.
+//!
+//! `RustCrypto`, on the other hand, tries to stick to Rust, which is great, but in doing so takes
+//! on more (unnecessary) dependencies and has a particularly unusable MSRV policy. Thus, its
+//! somewhat difficult to take on as a dependency.
+//!
+//! Instead, we go our own way here, and luckily actually implementing the required algorithms
+//! isn't all that difficult, at least if we're okay with performance being marginally sub-par.
+//! Because we don't ever do any signing, we don't need to worry about constant-time-ness, further
+//! reducing complexity.
+//!
+//! While we could similarly go our own way on hashing, too, rust-bitcoin's `bitcoin_hashes` crate
+//! does what we need without any unnecessary dependencies and with a very conservative MSRV
+//! policy. Thus we go ahead and use that for our hashing needs.
+
+pub mod hash;
pub mod ser;
pub mod query;
+#[cfg(feature = "validation")]
+mod crypto;
#[cfg(feature = "validation")]
pub mod validation;
pub mod ser;
pub mod query;
+#[cfg(feature = "validation")]
+mod crypto;
#[cfg(feature = "validation")]
pub mod validation;
impl Writer for Vec<u8> { fn write(&mut self, buf: &[u8]) { self.extend_from_slice(buf); } }
impl Writer for QueryBuf { fn write(&mut self, buf: &[u8]) { self.extend_from_slice(buf); } }
#[cfg(feature = "validation")]
-impl Writer for ring::digest::Context { fn write(&mut self, buf: &[u8]) { self.update(buf); } }
+impl Writer for crate::crypto::hash::Hasher { fn write(&mut self, buf: &[u8]) { self.update(buf); } }
pub(crate) fn write_name<W: Writer>(out: &mut W, name: &str) {
let canonical_name = name.to_ascii_lowercase();
if canonical_name == "." {
use ring::signature;
+use crate::crypto;
use crate::rr::*;
use crate::ser::write_name;
for ds in dses.clone() {
if ds.alg != dnskey.alg { continue; }
if dnskey.key_tag() == ds.key_tag {
- let alg = match ds.digest_type {
- 1 if trust_sha1 => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY,
- 2 => &ring::digest::SHA256,
- 4 => &ring::digest::SHA384,
+ let mut ctx = match ds.digest_type {
+ 1 if trust_sha1 => crypto::hash::Hasher::sha1(),
+ 2 => crypto::hash::Hasher::sha256(),
+ // TODO: 4 => crypto::hash::Hasher::sha384(),
_ => continue,
};
- let mut ctx = ring::digest::Context::new(alg);
write_name(&mut ctx, &dnskey.name);
ctx.update(&dnskey.flags.to_be_bytes());
ctx.update(&dnskey.protocol.to_be_bytes());