diff --git a/Cargo.toml b/Cargo.toml index e83152c1..f9020ae5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ [workspace] members = ["domain", "domain-core", "domain-resolv", "domain-sign", "domain-tsig", "domain-validate", "interop"] +[patch.crates-io] +ring = { git = "https://github.com/andrewtj/ring.git", rev = "eedb0514fb73e1034eefbec10140af8a51effff3"} \ No newline at end of file diff --git a/domain-sign/Cargo.toml b/domain-sign/Cargo.toml index b31fa991..1932cee9 100644 --- a/domain-sign/Cargo.toml +++ b/domain-sign/Cargo.toml @@ -19,14 +19,13 @@ path = "src/lib.rs" bytes = "0.4" derive_more = "^0.15" openssl = { version = "^0.10", optional = true } -ring = { version = "^0.14", optional = true } -untrusted = { version = "^0.6", optional = true } -unwrap = "^1.2" +ring = { version = "0.15.0-alpha", optional = true } +unwrap = "^1.2" [dependencies.domain-core] path = "../domain-core" version = "0.4.1" [features] -ringsigner = ["ring", "untrusted"] +ringsigner = ["ring"] default = ["ringsigner"] \ No newline at end of file diff --git a/domain-sign/src/ring.rs b/domain-sign/src/ring.rs index 4139988f..5bdaabeb 100644 --- a/domain-sign/src/ring.rs +++ b/domain-sign/src/ring.rs @@ -12,7 +12,6 @@ use ring::signature::{ EcdsaKeyPair, Ed25519KeyPair, KeyPair, RsaEncoding, RsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING }; -use untrusted::Input; use crate::key::SigningKey; @@ -39,7 +38,7 @@ impl<'a> Key<'a> { )?; let keypair = EcdsaKeyPair::from_pkcs8( &ECDSA_P256_SHA256_FIXED_SIGNING, - Input::from(pkcs8.as_ref()) + pkcs8.as_ref() )?; let public_key = keypair.public_key().as_ref()[1..].into(); Ok(Key { @@ -75,7 +74,7 @@ impl<'a> SigningKey for Key<'a> { fn sign(&self, msg: &[u8]) -> Result { match self.key { RingKey::Ecdsa(ref key) => { - Ok(Bytes::from(key.sign(self.rng, Input::from(msg))?.as_ref())) + Ok(Bytes::from(key.sign(self.rng, msg)?.as_ref())) } RingKey::Ed25519(ref key) => { Ok(Bytes::from(key.sign(msg).as_ref())) diff --git a/domain-tsig/Cargo.toml b/domain-tsig/Cargo.toml index d4d8b26a..1b600e59 100644 --- a/domain-tsig/Cargo.toml +++ b/domain-tsig/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] bytes = "^0.4" derive_more = "^0.14" -ring = "^0.14" +ring = "0.15.0-alpha" [dependencies.domain-core] path = "../domain-core" diff --git a/domain-tsig/src/lib.rs b/domain-tsig/src/lib.rs index 20bddb42..42ace10d 100644 --- a/domain-tsig/src/lib.rs +++ b/domain-tsig/src/lib.rs @@ -56,7 +56,7 @@ use std::{cmp, fmt, hash, mem, str}; use std::collections::HashMap; use bytes::{BigEndian, ByteOrder, Bytes, BytesMut}; use derive_more::Display; -use ring::{constant_time, digest, hmac, rand}; +use ring::{constant_time, hmac, rand, hkdf::KeyType}; use domain_core::iana::{Class, Rcode, TsigRcode}; use domain_core::message::Message; use domain_core::message_builder::{ @@ -99,7 +99,7 @@ use domain_core::rdata::rfc2845::{Time48, Tsig}; #[derive(Debug)] pub struct Key { /// The key’s bits and algorithm. - key: hmac::SigningKey, + key: hmac::Key, /// The name of the key as a domain name. name: Dname, @@ -149,7 +149,7 @@ impl Key { algorithm, min_mac_len, signing_len )?; Ok(Key { - key: hmac::SigningKey::new(algorithm.into_digest_algorithm(), key), + key: hmac::Key::new(algorithm.into_hmac_algorithm(), key), name, min_mac_len, signing_len @@ -173,14 +173,15 @@ impl Key { let (min_mac_len, signing_len) = Self::calculate_bounds( algorithm, min_mac_len, signing_len )?; - let algorithm = algorithm.into_digest_algorithm(); - let key_len = hmac::recommended_key_len(algorithm); + let algorithm = algorithm.into_hmac_algorithm(); + let key_len = algorithm.len(); let mut bytes = BytesMut::with_capacity(key_len); bytes.resize(key_len, 0); + rng.fill(&mut bytes)?; let key = Key { - key: hmac::SigningKey::generate_serializable( - algorithm, rng, bytes.as_mut() - )?, + key: hmac::Key::new( + algorithm, &bytes + ), name, min_mac_len, signing_len @@ -219,12 +220,12 @@ impl Key { } /// Creates a signing context for this key. - fn signing_context(&self) -> hmac::SigningContext { - hmac::SigningContext::with_key(&self.key) + fn signing_context(&self) -> hmac::Context { + hmac::Context::with_key(&self.key) } /// Returns a the possibly truncated slice of the signature. - fn signature_slice<'a>(&self, signature: &'a hmac::Signature) -> &'a [u8] { + fn signature_slice<'a>(&self, signature: &'a hmac::Tag) -> &'a [u8] { &signature.as_ref()[..self.signing_len] } } @@ -235,7 +236,7 @@ impl Key { impl Key { /// Returns the algorithm of this key. pub fn algorithm(&self) -> Algorithm { - Algorithm::from_digest_algorithm(self.key.digest_algorithm()) + Algorithm::from_hmac_algorithm(self.key.algorithm()) } /// Returns a reference to the name of this key. @@ -245,7 +246,7 @@ impl Key { /// Returns the native length of the signature from this key. pub fn native_len(&self) -> usize { - self.key.digest_algorithm().output_len + self.key.algorithm().len() } /// Returns the minimum acceptable length of a received signature. @@ -279,7 +280,7 @@ impl Key { /// acceptable by this key. fn compare_signatures( &self, - expected: &hmac::Signature, + expected: &hmac::Tag, provided: &[u8], ) -> Result<(), ValidationError> { if provided.len() < self.min_mac_len { @@ -885,7 +886,7 @@ impl> ServerSequence { #[derive(Clone, Debug)] struct SigningContext { /// The ring signing context. - context: hmac::SigningContext, + context: hmac::Context, /// The key. /// @@ -1101,7 +1102,7 @@ impl> SigningContext { key: K, message: &[u8], variables: &Variables - ) -> (Self, hmac::Signature) { + ) -> (Self, hmac::Tag) { let mut context = key.as_ref().signing_context(); context.update(message); variables.sign(key.as_ref(), &mut context); @@ -1120,7 +1121,7 @@ impl> SigningContext { &self, message: &[u8], variables: &Variables - ) -> hmac::Signature { + ) -> hmac::Tag { let mut context = self.context.clone(); context.update(message); variables.sign(self.key.as_ref(), &mut context); @@ -1134,7 +1135,7 @@ impl> SigningContext { mut self, message: &[u8], variables: &Variables - ) -> (hmac::Signature, K) { + ) -> (hmac::Tag, K) { self.context.update(message); variables.sign(self.key.as_ref(), &mut self.context); (self.context.sign(), self.key) @@ -1147,7 +1148,7 @@ impl> SigningContext { &mut self, message: &[u8], variables: &Variables - ) -> hmac::Signature { + ) -> hmac::Tag { // Replace current context with new context. let mut context = self.key().signing_context(); mem::swap(&mut self.context, &mut context); @@ -1173,7 +1174,7 @@ impl> SigningContext { &mut self, message: &[u8], variables: &Variables - ) -> hmac::Signature { + ) -> hmac::Tag { // Replace current context with new context. let mut context = self.key().signing_context(); mem::swap(&mut self.context, &mut context); @@ -1299,7 +1300,7 @@ impl Variables { /// Applies the variables to a signing context. /// /// This applies the full variables including key information. - fn sign(&self, key: &Key, context: &mut hmac::SigningContext) { + fn sign(&self, key: &Key, context: &mut hmac::Context) { let mut buf = [0u8; 8]; // Key name, in canonical wire format @@ -1337,7 +1338,7 @@ impl Variables { } /// Applies only the timing values to the signing context. - fn sign_timers(&self, context: &mut hmac::SigningContext) { + fn sign_timers(&self, context: &mut hmac::Context) { // Time Signed context.update(&self.time_signed.into_octets()); @@ -1383,34 +1384,30 @@ impl Algorithm { } } - /// Creates a value from a digest algorithm. + /// Creates a value from a HMAC algorithm. /// /// This will panic if `alg` is not one of the recognized algorithms. - fn from_digest_algorithm(alg: &'static digest::Algorithm) -> Self { - if *alg == digest::SHA1 { + fn from_hmac_algorithm(alg: hmac::Algorithm) -> Self { + if alg == hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY { Algorithm::Sha1 - } - else if *alg == digest::SHA256 { + } else if alg == hmac::HMAC_SHA256 { Algorithm::Sha256 - } - else if *alg == digest::SHA384 { + } else if alg == hmac::HMAC_SHA384 { Algorithm::Sha384 - } - else if *alg == digest::SHA512 { + } else if alg == hmac::HMAC_SHA512 { Algorithm::Sha512 - } - else { - panic!("Unknown TSIG key algorithm.") + } else { + panic!("Unknown TSIG key algorithm.") } } - /// Returns the ring digest algorithm for this TSIG algorithm. - fn into_digest_algorithm(self) -> &'static digest::Algorithm { + /// Returns the ring HMAC algorithm for this TSIG algorithm. + fn into_hmac_algorithm(self) -> hmac::Algorithm { match self { - Algorithm::Sha1 => &digest::SHA1, - Algorithm::Sha256 => &digest::SHA256, - Algorithm::Sha384 => &digest::SHA384, - Algorithm::Sha512 => &digest::SHA512, + Algorithm::Sha1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, + Algorithm::Sha256 => hmac::HMAC_SHA256, + Algorithm::Sha384 => hmac::HMAC_SHA384, + Algorithm::Sha512 => hmac::HMAC_SHA512, } } @@ -1435,7 +1432,7 @@ impl Algorithm { /// Returns the native length of a signature created with this algorithm. pub fn native_len(self) -> usize { - self.into_digest_algorithm().output_len + self.into_hmac_algorithm().len() } /// Returns the bounds for the allowed signature size. diff --git a/domain-validate/Cargo.toml b/domain-validate/Cargo.toml index 1381aaae..b7d7a519 100644 --- a/domain-validate/Cargo.toml +++ b/domain-validate/Cargo.toml @@ -18,8 +18,7 @@ path = "src/lib.rs" [dependencies] bytes = "0.4" derive_more = "^0.15" -ring = { version = "^0.14" } -untrusted = { version = "^0.6" } +ring = "=0.15.0-alpha3" [dependencies.domain-core] path = "../domain-core" diff --git a/domain-validate/src/lib.rs b/domain-validate/src/lib.rs index a06dc6ee..c86ad28f 100644 --- a/domain-validate/src/lib.rs +++ b/domain-validate/src/lib.rs @@ -9,7 +9,7 @@ use std::error; //------------ AlgorithmError ------------------------------------------------ /// An algorithm error during verification. -#[derive(Clone, Debug, Display)] +#[derive(Clone, Debug, Display, PartialEq)] pub enum AlgorithmError { #[display(fmt = "unsupported algorithm")] Unsupported, @@ -73,7 +73,7 @@ impl DnskeyExt for Dnskey { self.compose(&mut buf); let mut ctx = match algorithm { - DigestAlg::Sha1 => digest::Context::new(&digest::SHA1), + DigestAlg::Sha1 => digest::Context::new(&digest::SHA1_FOR_LEGACY_USE_ONLY), DigestAlg::Sha256 => digest::Context::new(&digest::SHA256), DigestAlg::Gost => { return Err(AlgorithmError::Unsupported); @@ -194,16 +194,14 @@ impl RrsigExt for Rrsig { let rrsig_labels = usize::from(self.labels()); let fqdn = rr.owner(); // Subtract the root label from count as the algorithm doesn't accomodate that. - let mut fqdn_labels = fqdn.iter_labels().count() - 1; + let fqdn_labels = fqdn.iter_labels().count() - 1; if rrsig_labels < fqdn_labels { // name = "*." | the rightmost rrsig_label labels of the fqdn b"\x01*".compose(buf); - let mut fqdn = fqdn.to_name(); - while fqdn_labels < rrsig_labels { - fqdn.parent(); - fqdn_labels -= 1; + match fqdn.to_name().iter_suffixes().skip(fqdn_labels - rrsig_labels).next() { + Some(name) => name.compose_canonical(buf), + None => fqdn.compose_canonical(buf), } - fqdn.compose_canonical(buf); } else { fqdn.compose_canonical(buf); } @@ -222,28 +220,20 @@ impl RrsigExt for Rrsig { dnskey: &Dnskey, signed_data: &Bytes, ) -> Result<(), AlgorithmError> { - use untrusted::Input; - - let message = untrusted::Input::from(signed_data); - let signature = Input::from(self.signature()); + let signature = self.signature(); match self.algorithm() { SecAlg::RsaSha1 | SecAlg::RsaSha1Nsec3Sha1 | SecAlg::RsaSha256 | SecAlg::RsaSha512 => { let algorithm = match self.algorithm() { - SecAlg::RsaSha1Nsec3Sha1 => &signature::RSA_PKCS1_2048_8192_SHA1, - SecAlg::RsaSha1 => &signature::RSA_PKCS1_2048_8192_SHA1, - SecAlg::RsaSha256 => &signature::RSA_PKCS1_2048_8192_SHA256, + SecAlg::RsaSha1 | SecAlg::RsaSha1Nsec3Sha1 => &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY, + SecAlg::RsaSha256 => &signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY, SecAlg::RsaSha512 => &signature::RSA_PKCS1_2048_8192_SHA512, _ => unreachable!(), }; // The key isn't available in either PEM or DER, so use the direct RSA verifier. - let (e, m) = dnskey.rsa_exponent_modulus()?; - signature::primitive::verify_rsa( - algorithm, - (Input::from(m), Input::from(e)), - message, - signature, - ) + let (e, n) = dnskey.rsa_exponent_modulus()?; + let public_key = signature::RsaPublicKeyComponents { n: &n, e: &e }; + public_key.verify(algorithm, &signed_data, &signature) .map_err(|_| AlgorithmError::BadSig) } SecAlg::EcdsaP256Sha256 | SecAlg::EcdsaP384Sha384 => { @@ -259,13 +249,13 @@ impl RrsigExt for Rrsig { key.push(0x4); key.extend_from_slice(&public_key); - signature::verify(algorithm, Input::from(&key), message, signature) - .map_err(|_| AlgorithmError::BadSig) + signature::UnparsedPublicKey::new(algorithm, &key).verify(&signed_data, &signature) + .map_err(|_| AlgorithmError::BadSig) } SecAlg::Ed25519 => { let key = dnskey.public_key(); - signature::verify(&signature::ED25519, Input::from(&key), message, signature) - .map_err(|_| AlgorithmError::BadSig) + signature::UnparsedPublicKey::new(&signature::ED25519, &key).verify(&signed_data, &signature) + .map_err(|_| AlgorithmError::BadSig) } _ => return Err(AlgorithmError::Unsupported), } @@ -278,7 +268,8 @@ impl RrsigExt for Rrsig { mod test { use super::*; use domain_core::iana::{Class, Rtype, SecAlg}; - use domain_core::{rdata::{MasterRecordData, Ds}, utils::base64, Dname}; + use domain_core::{rdata::*, utils::base64, master::scan::Scanner, Dname, Serial}; + use std::str::FromStr; // Returns current root KSK/ZSK for testing. fn root_pubkey() -> (Dnskey, Dnskey) { @@ -350,7 +341,7 @@ mod test { Dnskey::new(256, 3, SecAlg::EcdsaP256Sha256, base64::decode("oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==").unwrap().into()), ); - let owner = Dname::from_slice(b"\x0acloudflare\x03com\x00").unwrap(); + let owner = Dname::from_str("cloudflare.com.").unwrap(); let rrsig = Rrsig::new(Rtype::Dnskey, SecAlg::EcdsaP256Sha256, 2, 3600, 1560314494.into(), 1555130494.into(), 2371, owner.clone(), base64::decode("8jnAGhG7O52wmL065je10XQztRX1vK8P8KBSyo71Z6h5wAT9+GFxKBaEzcJBLvRmofYFDAhju21p1uTfLaYHrg==").unwrap().into()); rrsig_verify_dnskey(ksk, zsk, rrsig); } @@ -403,4 +394,28 @@ mod test { assert!(rrsig.verify_signed_data(&ksk, &signed_data).is_ok()); } + + // Parse RRSIG serial from text. + fn rrsig_serial(x: &str) -> Serial { + let mut s = Scanner::new(x); + Serial::scan_rrsig(&mut s).unwrap() + } + + #[test] + fn rrsig_verify_wildcard() { + let key = Dnskey::new(256, 3, SecAlg::RsaSha1, base64::decode("AQOy1bZVvpPqhg4j7EJoM9rI3ZmyEx2OzDBVrZy/lvI5CQePxXHZS4i8dANH4DX3tbHol61ek8EFMcsGXxKciJFHyhl94C+NwILQdzsUlSFovBZsyl/NX6yEbtw/xN9ZNcrbYvgjjZ/UVPZIySFNsgEYvh0z2542lzMKR4Dh8uZffQ==").unwrap().into()); + let rrsig = Rrsig::new(Rtype::Mx, SecAlg::RsaSha1, 2, 3600, rrsig_serial("20040509183619"), rrsig_serial("20040409183619"), 38519, Dname::from_str("example.").unwrap(), base64::decode("OMK8rAZlepfzLWW75Dxd63jy2wswESzxDKG2f9AMN1CytCd10cYISAxfAdvXSZ7xujKAtPbctvOQ2ofO7AZJ+d01EeeQTVBPq4/6KCWhqe2XTjnkVLNvvhnc0u28aoSsG0+4InvkkOHknKxw4kX18MMR34i8lC36SR5xBni8vHI=").unwrap().into()); + let record = Record::new(Dname::from_str("a.z.w.example.").unwrap(), Class::In, 3600, Mx::new(1, Dname::from_str("ai.example.").unwrap())); + let signed_data = { + let mut buf = Vec::new(); + rrsig.signed_data(&mut buf, &mut [record]); + Bytes::from(buf) + }; + + // Test that the key matches RRSIG + assert_eq!(key.key_tag(), rrsig.key_tag()); + + // Test verifier + assert_eq!(rrsig.verify_signed_data(&key, &signed_data), Ok(())); + } } diff --git a/interop/Cargo.toml b/interop/Cargo.toml index 57d47ee0..4219ec5f 100644 --- a/interop/Cargo.toml +++ b/interop/Cargo.toml @@ -11,5 +11,5 @@ domain-core = { path = "../domain-core" } domain-resolv = { path = "../domain-resolv" } domain-tsig = { path = "../domain-tsig" } bytes = "0.4" -ring = "0.14" +ring = "0.15.0-alpha"