diff --git a/doc/manual/build/man/dnst-keyset.1 b/doc/manual/build/man/dnst-keyset.1 index 45c3b28..682bf17 100644 --- a/doc/manual/build/man/dnst-keyset.1 +++ b/doc/manual/build/man/dnst-keyset.1 @@ -735,6 +735,16 @@ Not all values are supported for KMIP keys. For RSA keys, the length of the key in bits. .UNINDENT .IP \(bu 2 +ksk\-roll\-type +.sp +The type of KSK roll to use. Possible values are double\-signature\-ksk\-roll +and double\-ds\-ksk\-roll. +.IP \(bu 2 +zsk\-roll\-type +.sp +The type of ZSK roll to use. Possible values are pre\-publish\-zsk\-roll +and double\-signature\-zsk\-roll. +.IP \(bu 2 auto\-ksk, auto\-zsk, auto\-csk, auto\-algorithm .sp These commands take four boolean arguments: . diff --git a/doc/manual/source/man/dnst-keyset.rst b/doc/manual/source/man/dnst-keyset.rst index bcccf3c..fbab047 100644 --- a/doc/manual/source/man/dnst-keyset.rst +++ b/doc/manual/source/man/dnst-keyset.rst @@ -715,6 +715,16 @@ The keyset subcommand provides the following commands: For RSA keys, the length of the key in bits. + * ksk-roll-type + + The type of KSK roll to use. Possible values are double-signature-ksk-roll + and double-ds-ksk-roll. + + * zsk-roll-type + + The type of ZSK roll to use. Possible values are pre-publish-zsk-roll + and double-signature-zsk-roll. + * auto-ksk, auto-zsk, auto-csk, auto-algorithm These commands take four boolean arguments: . diff --git a/src/commands/keyset/cmd.rs b/src/commands/keyset/cmd.rs index e3d237e..0d77363 100644 --- a/src/commands/keyset/cmd.rs +++ b/src/commands/keyset/cmd.rs @@ -333,6 +333,20 @@ enum SetCommands { algorithm: String, }, + /// Set the type of KSK roll to perform. + KskRollType { + /// The type of KSK roll. + #[arg(value_parser = KskRollType::new)] + value: KskRollType, + }, + + /// Set the type of ZSK roll to perform. + ZskRollType { + /// The type of ZSK roll. + #[arg(value_parser = ZskRollType::new)] + value: ZskRollType, + }, + /// Set the config values for automatic KSK rolls. AutoKsk { /// Whether to automatically start a key roll. @@ -620,12 +634,18 @@ enum RollVariant { impl RollVariant { /// Return the right RollType for a RollVariant. - fn roll_variant_to_roll(self) -> RollType { + fn roll_variant_to_roll(self, config: &KeySetConfig) -> RollType { // For key types, such as KSK and ZSK, that can have different rolls, // we should find out which variant is used. match self { - RollVariant::Ksk => RollType::KskRoll, - RollVariant::Zsk => RollType::ZskRoll, + RollVariant::Ksk => match config.ksk_roll_type { + KskRollType::DoubleSignatureKskRoll => RollType::KskRoll, + KskRollType::DoubleDsKskRoll => RollType::KskDoubleDsRoll, + }, + RollVariant::Zsk => match config.zsk_roll_type { + ZskRollType::PrePublishZskRoll => RollType::ZskRoll, + ZskRollType::DoubleSignatureZskRoll => RollType::ZskDoubleSignatureRoll, + }, RollVariant::Csk => RollType::CskRoll, RollVariant::Algorithm => RollType::AlgorithmRoll, } @@ -698,6 +718,8 @@ impl Keyset { keys_dir, use_csk: false, algorithm: KeyParameters::EcdsaP256Sha256, + ksk_roll_type: KskRollType::DoubleSignatureKskRoll, + zsk_roll_type: ZskRollType::PrePublishZskRoll, ksk_validity: None, zsk_validity: None, csk_validity: None, @@ -1168,6 +1190,8 @@ impl Keyset { println!("state-file: {:?}", ws.config.state_file); println!("use-csk: {}", ws.config.use_csk); println!("algorithm: {}", ws.config.algorithm); + println!("ksk-roll-type: {}", ws.config.ksk_roll_type); + println!("zsk-roll-type: {}", ws.config.zsk_roll_type); println!("ksk-validity: {:?}", ws.config.ksk_validity); println!("zsk-validity: {:?}", ws.config.zsk_validity); println!("csk-validity: {:?}", ws.config.csk_validity); @@ -1641,6 +1665,14 @@ struct KeySetConfig { /// Algorithm and other parameters for key generation. algorithm: KeyParameters, + /// Type of KSK roll to perform. + #[serde(default)] + ksk_roll_type: KskRollType, + + /// Type of ZSK roll to perform. + #[serde(default)] + zsk_roll_type: ZskRollType, + /// Validity of KSKs. ksk_validity: Option, /// Validity of ZSKs. @@ -1712,6 +1744,78 @@ struct AutoConfig { done: bool, } +/// Type of KSK roll to perform. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +enum KskRollType { + #[default] + /// KSK roll that first adds the new KSK to the DNSKEY RRset and add an + /// additional signature over the DNSKEY RRset from that key before + /// replace the DS for the old key with one for the new key. + DoubleSignatureKskRoll, + + /// KSK roll that first publishes an additional DS record for the new + /// KSK before replacing the old KSK with the new KSK in the DNSKEY RRset + /// and signing the DNSKEY RRset with the new key. + DoubleDsKskRoll, +} + +impl KskRollType { + /// Create a new KskRollType based on the roll name. + fn new(roll: &str) -> Result { + if roll == "double-signature-ksk-roll" { + Ok(KskRollType::DoubleSignatureKskRoll) + } else if roll == "double-ds-ksk-roll" { + Ok(KskRollType::DoubleDsKskRoll) + } else { + Err(format!("unknown roll name {roll}\n").into()) + } + } +} + +impl Display for KskRollType { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + KskRollType::DoubleSignatureKskRoll => write!(fmt, "double-signature-ksk-roll"), + KskRollType::DoubleDsKskRoll => write!(fmt, "double-ds-ksk-roll"), + } + } +} + +/// Type of ZSK key roll to use. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +enum ZskRollType { + #[default] + /// ZSK roll where the new ZSK is first added to the DNSKEY + /// RRset and then the zone is signed with the new key. + PrePublishZskRoll, + + /// ZSK roll where the zone is signed with both the old and the + /// new ZSK for some period of time. + DoubleSignatureZskRoll, +} + +impl ZskRollType { + /// Create a new ZskRollType based on the roll name. + fn new(roll: &str) -> Result { + if roll == "pre-publish-zsk-roll" { + Ok(ZskRollType::PrePublishZskRoll) + } else if roll == "double-signature-zsk-roll" { + Ok(ZskRollType::DoubleSignatureZskRoll) + } else { + Err(format!("unknown roll name {roll}\n").into()) + } + } +} + +impl Display for ZskRollType { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ZskRollType::PrePublishZskRoll => write!(fmt, "pre-publish-zsk-roll"), + ZskRollType::DoubleSignatureZskRoll => write!(fmt, "double-signature-zsk-roll"), + } + } +} + /// Persistent state for the keyset command. #[derive(Deserialize, Serialize)] pub struct KeySetState { @@ -1949,6 +2053,12 @@ impl WorkSpace { SetCommands::Algorithm { algorithm, bits } => { self.config.algorithm = KeyParameters::new(&algorithm, bits)?; } + SetCommands::KskRollType { value } => { + self.config.ksk_roll_type = value; + } + SetCommands::ZskRollType { value } => { + self.config.zsk_roll_type = value; + } SetCommands::AutoKsk { start, report, @@ -2068,23 +2178,23 @@ impl WorkSpace { return Ok(()); } RollCommands::Propagation1Complete { ttl } => { - let roll = roll_variant.roll_variant_to_roll(); + let roll = roll_variant.roll_variant_to_roll(&self.config); self.state.keyset.propagation1_complete(roll, ttl) } RollCommands::CacheExpired1 => { - let roll = roll_variant.roll_variant_to_roll(); + let roll = roll_variant.roll_variant_to_roll(&self.config); self.state.keyset.cache_expired1(roll) } RollCommands::Propagation2Complete { ttl } => { - let roll = roll_variant.roll_variant_to_roll(); + let roll = roll_variant.roll_variant_to_roll(&self.config); self.state.keyset.propagation2_complete(roll, ttl) } RollCommands::CacheExpired2 => { - let roll = roll_variant.roll_variant_to_roll(); + let roll = roll_variant.roll_variant_to_roll(&self.config); self.state.keyset.cache_expired2(roll) } RollCommands::RollDone => { - let roll = roll_variant.roll_variant_to_roll(); + let roll = roll_variant.roll_variant_to_roll(&self.config); self.do_done(roll)?; self.state_changed = true; return Ok(()); @@ -3319,10 +3429,12 @@ impl WorkSpace { /// Start a KSK roll. fn start_ksk_roll(&mut self, env: &impl Env, verbose: bool) -> Result, Error> { + let roll_type = match self.config.ksk_roll_type { + KskRollType::DoubleSignatureKskRoll => RollType::KskRoll, + KskRollType::DoubleDsKskRoll => RollType::KskDoubleDsRoll, + }; let now = self.faketime_or_now(); - let roll_type = RollType::KskRoll; - assert!(!self.state.keyset.keys().is_empty()); // Check for CSK. @@ -3396,10 +3508,12 @@ impl WorkSpace { /// Start a ZSK roll. fn start_zsk_roll(&mut self, env: &impl Env, verbose: bool) -> Result, Error> { + let roll_type = match self.config.zsk_roll_type { + ZskRollType::PrePublishZskRoll => RollType::ZskRoll, + ZskRollType::DoubleSignatureZskRoll => RollType::ZskDoubleSignatureRoll, + }; let now = self.faketime_or_now(); - let roll_type = RollType::ZskRoll; - assert!(!self.state.keyset.keys().is_empty()); // Check for CSK. @@ -3435,8 +3549,6 @@ impl WorkSpace { .collect(); let old: Vec<_> = old_stored.iter().map(|name| name.as_ref()).collect(); - // Collect algorithms. Maybe this needs to be in the library. - // Create a new ZSK let (zsk_pub_url, zsk_priv_url, algorithm, key_tag) = self.new_keys(false, env)?; self.state