domain-validate: add support for 1024b RSA keys, upgrade ring to 0.15.0-alpha

This commit is contained in:
Marek Vavruša
2019-07-11 22:58:12 -07:00
parent 0adf9a496e
commit 4e8bfd7ac8
8 changed files with 91 additions and 80 deletions
+2
View File
@@ -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"}
+3 -4
View File
@@ -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"]
+2 -3
View File
@@ -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<Bytes, Self::Error> {
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()))
+1 -1
View File
@@ -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"
+38 -41
View File
@@ -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<K: AsRef<Key>> ServerSequence<K> {
#[derive(Clone, Debug)]
struct SigningContext<K> {
/// The ring signing context.
context: hmac::SigningContext,
context: hmac::Context,
/// The key.
///
@@ -1101,7 +1102,7 @@ impl<K: AsRef<Key>> SigningContext<K> {
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<K: AsRef<Key>> SigningContext<K> {
&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<K: AsRef<Key>> SigningContext<K> {
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<K: AsRef<Key>> SigningContext<K> {
&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<K: AsRef<Key>> SigningContext<K> {
&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.
+1 -2
View File
@@ -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"
+43 -28
View File
@@ -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(()));
}
}
+1 -1
View File
@@ -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"