mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-09-29 21:24:57 +02:00
domain-validate: add support for 1024b RSA keys, upgrade ring to 0.15.0-alpha
This commit is contained in:
@@ -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"}
|
||||
@@ -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"]
|
||||
@@ -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()))
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user