diff --git a/Cargo.lock b/Cargo.lock index 958daa20..6036a115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,7 +311,7 @@ dependencies = [ [[package]] name = "domain" version = "0.10.3" -source = "git+https://github.com/NLnetLabs/domain.git?branch=initial-nsec3-generation#5efcccfabd4535a40c8981fc36f89ca73899c3b4" +source = "git+https://github.com/NLnetLabs/domain.git?branch=main#0c6c36ee4974bad580024ce5f78965d5d8aaefbe" dependencies = [ "arc-swap", "bytes", diff --git a/Cargo.toml b/Cargo.toml index ff61413d..bb42941b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,14 +20,13 @@ ring = ["domain/ring"] bytes = "1.8.0" chrono = "0.4.38" clap = { version = "4.3.4", features = ["cargo", "derive"] } -domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "initial-nsec3-generation", features = [ +domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "main", features = [ "bytes", "net", "resolv", "tsig", "unstable-client-transport", "unstable-sign", - "unstable-validate", "unstable-validator", "zonefile", ] } @@ -43,6 +42,6 @@ _unused_lazy_static = { package = "lazy_static", version = "1.0.2" } test_bin = "0.4.0" tempfile = "3.14.0" regex = "1.11.1" -domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "initial-nsec3-generation", features = [ +domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "main", features = [ "unstable-stelline", ] } diff --git a/src/commands/key2ds.rs b/src/commands/key2ds.rs index 1475f97c..676cb8d7 100644 --- a/src/commands/key2ds.rs +++ b/src/commands/key2ds.rs @@ -5,11 +5,11 @@ use std::path::PathBuf; use clap::builder::ValueParser; use clap::Parser; -use domain::base::iana::{DigestAlg, SecAlg}; +use domain::base::iana::{DigestAlgorithm, SecurityAlgorithm}; use domain::base::zonefile_fmt::{DisplayKind, ZonefileFmt}; use domain::base::Record; +use domain::dnssec::validator::base::DnskeyExt; use domain::rdata::Ds; -use domain::validate::DnskeyExt; use domain::zonefile::inplace::{Entry, ScannedRecordData}; use lexopt::Arg; @@ -40,23 +40,24 @@ pub struct Key2ds { long = "algorithm", value_parser = ValueParser::new(parse_digest_alg) )] - algorithm: Option, + algorithm: Option, /// Keyfile to read #[arg()] keyfile: PathBuf, } -pub fn parse_digest_alg(arg: &str) -> Result { +pub fn parse_digest_alg(arg: &str) -> Result { if let Ok(num) = arg.parse() { - let alg = DigestAlg::from_int(num); + let alg = DigestAlgorithm::from_int(num); if alg.to_mnemonic().is_some() { Ok(alg) } else { Err(Error::from("unknown algorithm number")) } } else { - DigestAlg::from_mnemonic(arg.as_bytes()).ok_or(Error::from("unknown algorithm mnemonic")) + DigestAlgorithm::from_mnemonic(arg.as_bytes()) + .ok_or(Error::from("unknown algorithm mnemonic")) } } @@ -91,9 +92,9 @@ impl LdnsCommand for Key2ds { while let Some(arg) = parser.next()? { match arg { - Arg::Short('1') => algorithm = Some(DigestAlg::SHA1), - Arg::Short('2') => algorithm = Some(DigestAlg::SHA256), - Arg::Short('4') => algorithm = Some(DigestAlg::SHA384), + Arg::Short('1') => algorithm = Some(DigestAlgorithm::SHA1), + Arg::Short('2') => algorithm = Some(DigestAlgorithm::SHA256), + Arg::Short('4') => algorithm = Some(DigestAlgorithm::SHA384), Arg::Short('f') => ignore_sep = true, Arg::Short('n') => write_to_stdout = true, Arg::Value(val) => { @@ -169,7 +170,7 @@ impl Key2ds { .algorithm .unwrap_or_else(|| determine_hash_from_sec_alg(sec_alg)); - if digest_alg == DigestAlg::GOST { + if digest_alg == DigestAlgorithm::GOST { return Err("Error: the GOST algorithm is deprecated and must not be used. Try a different algorithm.".into()); } @@ -225,22 +226,22 @@ impl Key2ds { } } -fn determine_hash_from_sec_alg(sec_alg: SecAlg) -> DigestAlg { +fn determine_hash_from_sec_alg(sec_alg: SecurityAlgorithm) -> DigestAlgorithm { match sec_alg { - SecAlg::RSASHA256 - | SecAlg::RSASHA512 - | SecAlg::ED25519 - | SecAlg::ED448 - | SecAlg::ECDSAP256SHA256 => DigestAlg::SHA256, - SecAlg::ECDSAP384SHA384 => DigestAlg::SHA384, - SecAlg::ECC_GOST => DigestAlg::GOST, - _ => DigestAlg::SHA1, + SecurityAlgorithm::RSASHA256 + | SecurityAlgorithm::RSASHA512 + | SecurityAlgorithm::ED25519 + | SecurityAlgorithm::ED448 + | SecurityAlgorithm::ECDSAP256SHA256 => DigestAlgorithm::SHA256, + SecurityAlgorithm::ECDSAP384SHA384 => DigestAlgorithm::SHA384, + SecurityAlgorithm::ECC_GOST => DigestAlgorithm::GOST, + _ => DigestAlgorithm::SHA1, } } #[cfg(test)] mod test { - use domain::base::iana::DigestAlg; + use domain::base::iana::DigestAlgorithm; use tempfile::TempDir; use crate::commands::Command; @@ -321,7 +322,7 @@ mod test { assert_eq!( res, Key2ds { - algorithm: Some(DigestAlg::SHA1), + algorithm: Some(DigestAlgorithm::SHA1), ..base.clone() } ); @@ -330,7 +331,7 @@ mod test { assert_eq!( res, Key2ds { - algorithm: Some(DigestAlg::SHA1), + algorithm: Some(DigestAlgorithm::SHA1), ..base.clone() } ); @@ -339,7 +340,7 @@ mod test { assert_eq!( res, Key2ds { - algorithm: Some(DigestAlg::SHA1), + algorithm: Some(DigestAlgorithm::SHA1), ..base.clone() } ); @@ -393,7 +394,7 @@ mod test { assert_eq!( res, Key2ds { - algorithm: Some(DigestAlg::SHA1), + algorithm: Some(DigestAlgorithm::SHA1), ..base.clone() } ); @@ -404,7 +405,7 @@ mod test { Key2ds { ignore_sep: true, write_to_stdout: true, - algorithm: Some(DigestAlg::SHA1), + algorithm: Some(DigestAlgorithm::SHA1), ..base.clone() } ); diff --git a/src/commands/keygen.rs b/src/commands/keygen.rs index 724d32b4..86fd91c6 100644 --- a/src/commands/keygen.rs +++ b/src/commands/keygen.rs @@ -5,12 +5,12 @@ use std::path::Path; use clap::builder::ValueParser; use clap::ValueEnum; -use domain::base::iana::{DigestAlg, SecAlg}; +use domain::base::iana::{DigestAlgorithm, SecurityAlgorithm}; use domain::base::name::Name; use domain::base::zonefile_fmt::{DisplayKind, ZonefileFmt}; -use domain::sign::crypto::common; -use domain::sign::crypto::common::GenerateParams; -use domain::validate::Key; +use domain::crypto::sign::GenerateParams; +use domain::dnssec::validator::base::DnskeyExt; +use domain::rdata::Ds; use lexopt::Arg; use crate::env::Env; @@ -169,11 +169,11 @@ impl LdnsCommand for Keygen { algorithm = parse_os_with("algorithm (-a)", &value, |s| { Ok(match s { - "RSASHA256" | "8" => Some(SecAlg::RSASHA256), - "ECDSAP256SHA256" | "13" => Some(SecAlg::ECDSAP256SHA256), - "ECDSAP384SHA384" | "14" => Some(SecAlg::ECDSAP384SHA384), - "ED25519" | "15" => Some(SecAlg::ED25519), - "ED448" | "16" => Some(SecAlg::ED448), + "RSASHA256" | "8" => Some(SecurityAlgorithm::RSASHA256), + "ECDSAP256SHA256" | "13" => Some(SecurityAlgorithm::ECDSAP256SHA256), + "ECDSAP384SHA384" | "14" => Some(SecurityAlgorithm::ECDSAP384SHA384), + "ED25519" | "15" => Some(SecurityAlgorithm::ED25519), + "ED448" | "16" => Some(SecurityAlgorithm::ED448), _ => { return Err("unknown algorithm mnemonic or number"); @@ -227,11 +227,11 @@ impl LdnsCommand for Keygen { } let algorithm = match algorithm { - Some(SecAlg::RSASHA256) => GenerateParams::RsaSha256 { bits }, - Some(SecAlg::ECDSAP256SHA256) => GenerateParams::EcdsaP256Sha256, - Some(SecAlg::ECDSAP384SHA384) => GenerateParams::EcdsaP384Sha384, - Some(SecAlg::ED25519) => GenerateParams::Ed25519, - Some(SecAlg::ED448) => GenerateParams::Ed448, + Some(SecurityAlgorithm::RSASHA256) => GenerateParams::RsaSha256 { bits }, + Some(SecurityAlgorithm::ECDSAP256SHA256) => GenerateParams::EcdsaP256Sha256, + Some(SecurityAlgorithm::ECDSAP384SHA384) => GenerateParams::EcdsaP384Sha384, + Some(SecurityAlgorithm::ED25519) => GenerateParams::Ed25519, + Some(SecurityAlgorithm::ED448) => GenerateParams::Ed448, Some(_) => unreachable!(), None => { return Err("Missing algorithm (-a) option".into()); @@ -307,25 +307,24 @@ impl Keygen { // The digest algorithm is selected based on the key algorithm. let digest_alg = match params.algorithm() { - SecAlg::RSASHA256 => DigestAlg::SHA256, - SecAlg::ECDSAP256SHA256 => DigestAlg::SHA256, - SecAlg::ECDSAP384SHA384 => DigestAlg::SHA384, - SecAlg::ED25519 => DigestAlg::SHA256, - SecAlg::ED448 => DigestAlg::SHA256, + SecurityAlgorithm::RSASHA256 => DigestAlgorithm::SHA256, + SecurityAlgorithm::ECDSAP256SHA256 => DigestAlgorithm::SHA256, + SecurityAlgorithm::ECDSAP384SHA384 => DigestAlgorithm::SHA384, + SecurityAlgorithm::ED25519 => DigestAlgorithm::SHA256, + SecurityAlgorithm::ED448 => DigestAlgorithm::SHA256, _ => unreachable!(), }; // Generate the key. // TODO: Attempt repeated generation to avoid key tag collisions. - let (secret_key, public_key) = common::generate(params) - .map_err(|err| format!("an implementation error occurred: {err}").into()) - .context("generating a cryptographic keypair")?; // TODO: Add a high-level operation in 'domain' to select flags? let flags = if self.make_ksk { 257 } else { 256 }; - let public_key = Key::new(self.name.clone(), flags, public_key); + let (secret_key, public_key) = domain::crypto::sign::generate(params, flags) + .map_err(|err| format!("an implementation error occurred: {err}").into()) + .context("generating a cryptographic keypair")?; let digest = self.make_ksk.then(|| { public_key - .digest(digest_alg) + .digest(&self.name, digest_alg) .expect("only supported digest algorithms are used") }); @@ -354,13 +353,21 @@ impl Keygen { } // Prepare the contents to write. + let key_tag = public_key.key_tag(); + let algorithm = public_key.algorithm(); let secret_key = secret_key.display_as_bind().to_string(); - let public_key = public_key.display_as_bind().to_string(); + let public_key = format!( + "{} IN DNSKEY {}", + self.name.fmt_with_dot(), + public_key.display_zonefile(DisplayKind::Simple) + ); let digest = digest.map(|digest| { format!( "{} IN DS {}\n", self.name.fmt_with_dot(), - digest.display_zonefile(DisplayKind::Simple) + Ds::new(key_tag, algorithm, digest_alg, digest) + .expect("we generated the digest, so don't expect it to be too long") + .display_zonefile(DisplayKind::Simple) ) }); @@ -411,7 +418,7 @@ impl Keygen { #[cfg(test)] mod test { - use domain::sign::crypto::common::GenerateParams; + use domain::crypto::sign::GenerateParams; use regex::Regex; use crate::commands::Command; diff --git a/src/commands/nsec3hash.rs b/src/commands/nsec3hash.rs index 906472ae..eacea5bf 100644 --- a/src/commands/nsec3hash.rs +++ b/src/commands/nsec3hash.rs @@ -2,10 +2,10 @@ use std::ffi::OsString; use std::str::FromStr; use clap::builder::ValueParser; -use domain::base::iana::nsec3::Nsec3HashAlg; +use domain::base::iana::nsec3::Nsec3HashAlgorithm; use domain::base::name::Name; +use domain::dnssec::common::nsec3_hash; use domain::rdata::nsec3::Nsec3Salt; -use domain::validate::nsec3_hash; use lexopt::Arg; use crate::env::Env; @@ -25,7 +25,7 @@ pub struct Nsec3Hash { default_value = "SHA-1", value_parser = ValueParser::new(Nsec3Hash::parse_nsec3_alg) )] - algorithm: Nsec3HashAlg, + algorithm: Nsec3HashAlgorithm, /// The number of hash iterations #[arg( @@ -66,7 +66,7 @@ impl LdnsCommand for Nsec3Hash { const COMPATIBLE_VERSION: &'static str = "1.8.4"; fn parse_ldns>(args: I) -> Result { - let mut algorithm = Nsec3HashAlg::SHA1; + let mut algorithm = Nsec3HashAlgorithm::SHA1; let mut iterations = 1; let mut salt = Nsec3Salt::empty(); let mut name = None; @@ -127,23 +127,23 @@ impl Nsec3Hash { } } - pub fn parse_nsec3_alg(arg: &str) -> Result { + pub fn parse_nsec3_alg(arg: &str) -> Result { if let Ok(num) = arg.parse() { Self::num_to_nsec3_alg(num) } else { - Nsec3HashAlg::from_mnemonic(arg.as_bytes()).ok_or("unknown algorithm mnemonic") + Nsec3HashAlgorithm::from_mnemonic(arg.as_bytes()).ok_or("unknown algorithm mnemonic") } } - pub fn parse_nsec3_alg_as_num(arg: &str) -> Result { + pub fn parse_nsec3_alg_as_num(arg: &str) -> Result { match arg.parse() { Ok(num) => Self::num_to_nsec3_alg(num), Err(_) => Err("malformed algorithm number"), } } - pub fn num_to_nsec3_alg(num: u8) -> Result { - let alg = Nsec3HashAlg::from_int(num); + pub fn num_to_nsec3_alg(num: u8) -> Result { + let alg = Nsec3HashAlgorithm::from_int(num); match alg.to_mnemonic() { Some(_) => Ok(alg), None => Err("unknown algorithm number"), @@ -171,7 +171,7 @@ mod tests { mod without_cli { use core::str::FromStr; - use domain::base::iana::Nsec3HashAlg; + use domain::base::iana::Nsec3HashAlgorithm; use domain::base::Name; use domain::rdata::nsec3::Nsec3Salt; @@ -196,7 +196,7 @@ mod tests { // We don't test all permutations as that would take too long (~20 seconds) #[allow(clippy::single_element_loop)] for algorithm in ["SHA-1"] { - let algorithm = Nsec3HashAlg::from_mnemonic(algorithm.as_bytes()) + let algorithm = Nsec3HashAlgorithm::from_mnemonic(algorithm.as_bytes()) .unwrap_or_else(|| panic!("Algorithm '{algorithm}' was expected to be okay")); let nsec3_hash = Nsec3Hash { algorithm, @@ -209,7 +209,7 @@ mod tests { for iterations in [0, 1, u16::MAX - 1, u16::MAX] { let nsec3_hash = Nsec3Hash { - algorithm: Nsec3HashAlg::SHA1, + algorithm: Nsec3HashAlgorithm::SHA1, iterations, salt: Nsec3Salt::empty(), name: Name::root(), @@ -221,7 +221,7 @@ mod tests { let salt = Nsec3Salt::from_str(salt) .unwrap_or_else(|err| panic!("Salt '{salt}' was expected to be okay: {err}")); let nsec3_hash = Nsec3Hash { - algorithm: Nsec3HashAlg::SHA1, + algorithm: Nsec3HashAlgorithm::SHA1, iterations: 0, salt, name: Name::root(), @@ -236,7 +236,7 @@ mod tests { let name = Name::from_str(name) .unwrap_or_else(|err| panic!("Name '{name}' was expected to be okay: {err}")); let nsec3_hash = Nsec3Hash { - algorithm: Nsec3HashAlg::SHA1, + algorithm: Nsec3HashAlgorithm::SHA1, iterations: 0, salt: Nsec3Salt::empty(), name,