Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
] }
Expand All @@ -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",
] }
51 changes: 26 additions & 25 deletions src/commands/key2ds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -40,23 +40,24 @@ pub struct Key2ds {
long = "algorithm",
value_parser = ValueParser::new(parse_digest_alg)
)]
algorithm: Option<DigestAlg>,
algorithm: Option<DigestAlgorithm>,

/// Keyfile to read
#[arg()]
keyfile: PathBuf,
}

pub fn parse_digest_alg(arg: &str) -> Result<DigestAlg, Error> {
pub fn parse_digest_alg(arg: &str) -> Result<DigestAlgorithm, Error> {
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"))
}
}

Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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());
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -321,7 +322,7 @@ mod test {
assert_eq!(
res,
Key2ds {
algorithm: Some(DigestAlg::SHA1),
algorithm: Some(DigestAlgorithm::SHA1),
..base.clone()
}
);
Expand All @@ -330,7 +331,7 @@ mod test {
assert_eq!(
res,
Key2ds {
algorithm: Some(DigestAlg::SHA1),
algorithm: Some(DigestAlgorithm::SHA1),
..base.clone()
}
);
Expand All @@ -339,7 +340,7 @@ mod test {
assert_eq!(
res,
Key2ds {
algorithm: Some(DigestAlg::SHA1),
algorithm: Some(DigestAlgorithm::SHA1),
..base.clone()
}
);
Expand Down Expand Up @@ -393,7 +394,7 @@ mod test {
assert_eq!(
res,
Key2ds {
algorithm: Some(DigestAlg::SHA1),
algorithm: Some(DigestAlgorithm::SHA1),
..base.clone()
}
);
Expand All @@ -404,7 +405,7 @@ mod test {
Key2ds {
ignore_sep: true,
write_to_stdout: true,
algorithm: Some(DigestAlg::SHA1),
algorithm: Some(DigestAlgorithm::SHA1),
..base.clone()
}
);
Expand Down
61 changes: 34 additions & 27 deletions src/commands/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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")
});

Expand Down Expand Up @@ -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)
)
});

Expand Down Expand Up @@ -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;
Expand Down
28 changes: 14 additions & 14 deletions src/commands/nsec3hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(
Expand Down Expand Up @@ -66,7 +66,7 @@ impl LdnsCommand for Nsec3Hash {
const COMPATIBLE_VERSION: &'static str = "1.8.4";

fn parse_ldns<I: IntoIterator<Item = OsString>>(args: I) -> Result<Args, Error> {
let mut algorithm = Nsec3HashAlg::SHA1;
let mut algorithm = Nsec3HashAlgorithm::SHA1;
let mut iterations = 1;
let mut salt = Nsec3Salt::empty();
let mut name = None;
Expand Down Expand Up @@ -127,23 +127,23 @@ impl Nsec3Hash {
}
}

pub fn parse_nsec3_alg(arg: &str) -> Result<Nsec3HashAlg, &'static str> {
pub fn parse_nsec3_alg(arg: &str) -> Result<Nsec3HashAlgorithm, &'static str> {
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<Nsec3HashAlg, &'static str> {
pub fn parse_nsec3_alg_as_num(arg: &str) -> Result<Nsec3HashAlgorithm, &'static str> {
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<Nsec3HashAlg, &'static str> {
let alg = Nsec3HashAlg::from_int(num);
pub fn num_to_nsec3_alg(num: u8) -> Result<Nsec3HashAlgorithm, &'static str> {
let alg = Nsec3HashAlgorithm::from_int(num);
match alg.to_mnemonic() {
Some(_) => Ok(alg),
None => Err("unknown algorithm number"),
Expand Down Expand Up @@ -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;

Expand All @@ -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,
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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,
Expand Down