diff options
author | Neal H. Walfield <neal@pep.foundation> | 2022-03-09 11:09:25 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2022-06-14 10:58:57 +0200 |
commit | 36270e24d2761a40e92668ab0d083afae322da4c (patch) | |
tree | 9097180fcd418fb047cd5f1429eaf3377d02c885 | |
parent | 429887d57f705e81e305008cfc95162432d7e684 (diff) |
openpgp: Add a subkey builder.
- Add `KeyBuilder` and `SubkeyBuilder` for creating a key, and
attaching a subkey to a certificate.
- See #483.
-rw-r--r-- | openpgp/NEWS | 2 | ||||
-rw-r--r-- | openpgp/src/cert.rs | 2 | ||||
-rw-r--r-- | openpgp/src/cert/builder.rs | 16 | ||||
-rw-r--r-- | openpgp/src/cert/builder/key.rs | 1015 | ||||
-rw-r--r-- | openpgp/src/cert/prelude.rs | 2 |
5 files changed, 1032 insertions, 5 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS index 20154a1e..ba6d00b2 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -12,6 +12,8 @@ PublicKeyAlgorithm}'s Display implementation now provides short names by default. The long descriptions are provided by the alternate formatter (e.g. =format!("{:#}", ...)=) + - cert::KeyBuilder + - cert::SubkeyBuilder ** Deprecated functionality - Error::UnsupportedCert, use Error::UnsupportedCert2 instead - DataFormat::MIME, no replacement, see #863 for details diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index 562ca652..62ff3e20 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -185,7 +185,7 @@ pub mod bundle; mod parser; mod revoke; -pub use self::builder::{CertBuilder, CipherSuite}; +pub use self::builder::{CertBuilder, CipherSuite, KeyBuilder, SubkeyBuilder}; pub use parser::{ CertParser, diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index a160a074..3593fdc2 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -3,9 +3,11 @@ use std::marker::PhantomData; use crate::packet; use crate::packet::{ - key, Key, key::Key4, + key::KeyRole, + key::SecretKey as KeySecretKey, + key::SecretParts as KeySecretParts, }; use crate::Result; use crate::packet::Signature; @@ -26,6 +28,12 @@ use crate::types::{ RevocationKey, }; +mod key; +pub use key::{ + KeyBuilder, + SubkeyBuilder, +}; + /// Groups symmetric and asymmetric algorithms. /// /// This is used to select a suite of ciphers. @@ -133,8 +141,8 @@ impl CipherSuite { } fn generate_key<K, R>(self, flags: K) - -> Result<Key<key::SecretParts, R>> - where R: key::KeyRole, + -> Result<Key<KeySecretParts, R>> + where R: KeyRole, K: AsRef<KeyFlags>, { use crate::types::Curve; @@ -1481,7 +1489,7 @@ impl CertBuilder<'_> { /// Creates the primary key and a direct key signature. fn primary_key(&self, creation_time: std::time::SystemTime) - -> Result<(key::SecretKey, Signature, Box<dyn Signer>)> + -> Result<(KeySecretKey, Signature, Box<dyn Signer>)> { let mut key = self.primary.ciphersuite .unwrap_or(self.ciphersuite) diff --git a/openpgp/src/cert/builder/key.rs b/openpgp/src/cert/builder/key.rs new file mode 100644 index 00000000..e23e463a --- /dev/null +++ b/openpgp/src/cert/builder/key.rs @@ -0,0 +1,1015 @@ +use std::time::{Duration, SystemTime}; + +use crate::packet::{ + Key, + key, +}; + +use crate::Result; +use crate::Packet; +use crate::packet::signature::{ + SignatureBuilder, + SIG_BACKDATE_BY, + subpacket::SubpacketTag, +}; +use crate::cert::prelude::*; +use crate::Error; +use crate::crypto::{Password, Signer}; +use crate::types::{ + HashAlgorithm, + KeyFlags, + SignatureType, +}; + +/// A Key builder. +/// +/// A `KeyBuilder` is used to create a key, which can then be attached +/// to an existing certificate as a subkey using +/// [`KeyBuilder::subkey`]. +/// +/// # Examples +/// +/// Generate a signing key and attach it to a certificate: +/// +/// ``` +/// use sequoia_openpgp as openpgp; +/// use openpgp::cert::prelude::*; +/// use openpgp::policy::StandardPolicy; +/// use openpgp::types::KeyFlags; +/// +/// # fn main() -> openpgp::Result<()> { +/// let p = &StandardPolicy::new(); +/// +/// # let (cert, _) = +/// # CertBuilder::general_purpose(None, Some("alice@example.org")) +/// # .generate()?; +/// # +/// let vc = cert.with_policy(p, None)?; +/// # let vc1 = vc.clone(); +/// let cert_new = KeyBuilder::new(KeyFlags::empty().set_signing()) +/// .subkey(vc)? +/// .attach_cert()?; +/// # let vc2 = cert_new.with_policy(p, None)?; +/// # assert_eq!(vc1.keys().count() + 1, vc2.keys().count()); +/// # Ok(()) +/// # } +/// ``` +pub struct KeyBuilder { + flags: KeyFlags, + cipher_suite: CipherSuite, + password: Option<Password>, + creation_time: Option<SystemTime>, +} +assert_send_and_sync!(KeyBuilder); + +impl KeyBuilder { + /// Returns a new `KeyBuilder`. + /// + /// Use [`KeyBuilder::subkey`] to generate a subkey and get a + /// [`SubkeyBuilder`], which can be used to add the subkey to a + /// certificate. + pub fn new(flags: KeyFlags) -> Self { + KeyBuilder { + flags, + cipher_suite: Default::default(), + creation_time: None, + password: None, + } + } + + /// Returns the selected cipher suite. + pub fn cipher_suite(&self) -> CipherSuite { + self.cipher_suite + } + + /// Sets the cipher suite. + pub fn set_cipher_suite(mut self, cipher_suite: CipherSuite) -> Self { + self.cipher_suite = cipher_suite; + self + } + + /// Returns the creation time. + /// + /// Returns `None` if the creation time hasn't been specified. In + /// that case, the creation time will be set to the current time + /// when the key material is generated by [`KeyBuilder::subkey`]. + pub fn creation_time(&self) -> Option<SystemTime> { + self.creation_time + } + + /// Sets the creation time. + /// + /// If `None`, then the creation time will be set to the current + /// time when the key material is generated by + /// [`KeyBuilder::subkey`]. + pub fn set_creation_time<T>(mut self, creation_time: T) -> Self + where T: Into<Option<SystemTime>> + { + self.creation_time = creation_time.into(); + self + } + + /// Returns the password, if any. + pub fn password(&self) -> Option<&Password> { + self.password.as_ref() + } + + /// Sets the password. + pub fn set_password<T>(mut self, password: T) -> Self + where T: Into<Option<Password>> + { + self.password = password.into(); + self + } + + /// Generates a key, and returns a `SubkeyBuilder`. + /// + /// The [`SubkeyBuilder`] will add the key to the specified + /// certificate. + /// + /// If the key creation time has not been explicitly set using + /// [`KeyBuilder::set_creation_time`], then the key's creation + /// time is set to the current time minus a few seconds. + /// + /// Setting the creation time to a short time in the past solves + /// two problems. First, when a new binding signature is created, + /// it must have a newer time than the previous binding signature. + /// This policy ensures that if a second binding signature is + /// immediately created after the key is created it does not need + /// to be postdated and thus can be used immediately. Second, if + /// the key is immediately transferred to another computer and its + /// clock is not quite synchronized, the key may appear to have + /// been created in the future and will thus be ignored. Although + /// NTP is widely used, emperically it seems that some virtual + /// machines have laggy clocks. + pub fn subkey(self, vc: ValidCert) -> Result<SubkeyBuilder<'_>> { + let mut key: Key<key::SecretParts, key::SubordinateRole> + = self.cipher_suite.generate_key(&self.flags)?; + let ct = self.creation_time.unwrap_or_else(|| { + crate::now() - Duration::new(SIG_BACKDATE_BY, 0) + }); + key.set_creation_time(ct)?; + + let signer = key.clone().into_keypair().unwrap(); + + if let Some(ref password) = self.password { + key.secret_mut().encrypt_in_place(password)?; + } + + let mut builder = SubkeyBuilder::new( + vc, key.parts_into_unspecified(), self.flags)?; + builder = builder.set_signature_creation_time(ct)?; + Ok(builder.set_subkey_signer(signer)) + } +} + +/// A Subkey builder. +/// +/// This builder simplifies attaching a subkey to a certificate, or +/// updating an existing subkey's binding signature. It is a more +/// high-level variant of [`SignatureBuilder`], which should be used +/// if more control is needed than this builder provides. +/// +/// # Security Considerations: Key Expiration +/// +/// **It is essential that keys have reasonable expiration times.** If +/// a binding signature is accidentally published without an +/// expiration time, it is effectively impossible to retract this by +/// publishing a new binding signature that has an expiration. This +/// is because an attacker may be able to withhold the newer binding +/// signature thereby causing a victim to use a key that is actually +/// expired. +/// +/// The heuristic described below takes this security consideration +/// into account. However, because the heuristic never extends a +/// key's expiration on its own, there are still cases where it is +/// necessary to set the expiration manually. +/// +/// # Binding Signature +/// +/// To attach a subkey to a certificate, the primary key needs to +/// issue a [subkey binding signature]. This binding signature +/// provides information about the key including its validity period +/// (i.e., when it expires), and may contain auxiliary information +/// like notations. A subkey binding signature usually contains the +/// following information: +/// +/// [subkey binding signature]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.1 +/// +/// - [Signature creation time](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.4) +/// +/// - [Key flags](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.21) +/// +/// - [Issuer](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.5) and Issuer Fingerprint. +/// +/// - [Primary key binding signature](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.26) (if the key is signing capable) +/// +/// The following information is also meaningful in the context of +/// a subkey binding signature: +/// +/// - [Key expiration time](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.6) +/// (relative to the key's creation time, not the signature's +/// creation time!) +/// +/// - [Signature exiration time](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.10) +/// +/// - [Exportable certification](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.11) +/// +/// - [Notations](https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.16) +/// +/// Because a `SubkeyBuilder` is just a wrapper around a +/// [`SignatureBuilder`], refer [`SignatureBuilder`]'s documentation +/// about to understand how some of these subpackets are automatically +/// set. +/// +/// It is possible to change the signature's creation time and key +/// expiration time using the +/// [`SubkeyBuilder::set_signature_creation_time`] and +/// [`SubkeyBuilder::set_key_expiration_time`] methods. Other +/// subpackets can be modified using +/// [`SubkeyBuilder::with_signature_template`]. +/// +/// ## Heuristic +/// +/// This builder uses a heuristic to select a binding signature to use +/// as a template and to select a key expiration. It is possible to +/// use your own binding signature by calling +/// [`SubkeyBuilder::set_signature_template`], and override the key +/// expiration time using [`SubkeyBuilder::set_key_expiration_time`]. +/// In general, you should use an existing binding signature as a +/// template to preserve any customizations that the user may have +/// made. +/// +/// Because forgetting to set an expiration time can be security +/// relevant, this heuristic acts conservatively. If possible, the +/// user interface should show the expiration time, and allow the user +/// to adjust it manually. +/// +/// The heuristic is: +/// +/// - If the subkey is already present on the certificate, the default +/// binding signature is based on the subkey's active binding +/// signature, and the key expiration time is reused. +/// +/// If the key would expire before the binding signature becomes +/// valid then [`SubkeyBuilder::attach`] will fail. +/// +/// Note: if the subkey is present, but it does not have a valid +/// binding signature, then the subkey is treated as a new subkey. +/// +/// - If the subkey is new, then the active binding signature of the +/// newest live, non-revoked, valid subkey is used as the binding +/// signature template. Newest means the the key with the latest +/// Key Creation Time and not necessarily the newest binding +/// signature. (If multiple keys have the same key creation time, +/// the key to use is chosen in an undefined, but deterministic +/// manner.) +/// +/// If the certificate does not have a subkey, then a default +/// binding signature is created. In this case, the default +/// expiration is set to the same expiration as the primary key, if +/// any. +/// +/// As above, if the key would expire before the binding signature +/// becomes valid then [`SubkeyBuilder::attach`] will fail. +/// +/// # Examples +/// +/// Add a new, signing-capable subkey to a certificate: +/// +/// ``` +/// use sequoia_openpgp as openpgp; +/// use openpgp::cert::prelude::*; +/// use openpgp::policy::StandardPolicy; +/// use openpgp::types::KeyFlags; +/// +/// # fn main() -> openpgp::Result<()> { +/// let p = &StandardPolicy::new(); +/// +/// # let (cert, _) = +/// # CertBuilder::general_purpose(None, Some("alice@example.org")) +/// # .generate()?; +/// # +/// let vc = cert.with_policy(p, None)?; +/// # let vc1 = vc.clone(); +/// let cert_new = KeyBuilder::new(KeyFlags::empty().set_signing()) +/// .subkey(vc)? +/// .attach_cert()?; +/// # let vc2 = cert_new.with_policy(p, None)?; +/// # assert_eq!(vc1.keys().count() + 1, vc2.keys().count()); +/// # Ok(()) +/// # } +/// ``` +/// +/// Import a raw encryption key: +/// +/// ``` +/// use std::time::SystemTime; +/// +/// use sequoia_openpgp as openpgp; +/// use openpgp::cert::prelude::*; +/// use openpgp::packet::Key; +/// use openpgp::packet::key::Key4; +/// use openpgp::policy::StandardPolicy; +/// use openpgp::types::KeyFlags; +/// +/// # fn main() -> openpgp::Result<()> { +/// let p = &StandardPolicy::new(); +/// +/// # let q = b"\x57\x15\x45\x1B\x68\xA5\x13\xA2\x20\x0F\x71\x9D\xE3\x05\x3B\xED\xA2\x21\xDE\x61\x5A\xF5\x67\x45\xBB\x97\x99\x43\x53\x59\x7C\x3F"; +/// let k: Key<_, _> +/// = Key4::import_public_ed25519(q, SystemTime::now())?.into(); +/// +/// # let (cert, _) = CertBuilder::new().generate()?; +/// # +/// let vc = cert.with_policy(p, None)?; +/// # let vc1 = vc.clone(); +/// let mut cert2 = SubkeyBuilder::new( +/// vc, k.parts_into_unspecified(), +/// KeyFlags::empty().set_transport_encryption())? +/// .attach_cert()?; +/// # +/// # let vc2 = cert2.with_policy(p, None)?; +/// # assert_eq!(vc1.keys().count() + 1, vc2.keys().count()); +/// # Ok(()) +/// # } +/// ``` +/// +/// Change all valid, non-revoked subkeys to expire in a year from now: +/// +/// ``` +/// use std::time::{SystemTime, Duration}; +/// +/// use sequoia_openpgp as openpgp; +/// use openpgp::cert::prelude::*; +/// use openpgp::Packet; +/// use openpgp::policy::StandardPolicy; +/// use openpgp::types::KeyFlags; +/// +/// # fn main() -> openpgp::Result<()> { +/// let p = &StandardPolicy::new(); +/// +/// let now = SystemTime::now(); +/// let e = now + Duration::new(365 * 24 * 60 * 60, 0); +/// +/// # let v = Duration::new(24 * 60 * 60, 0); +/// # let (cert, _) = +/// # CertBuilder::new() +/// # .set_creation_time(now - Duration::new(60, 0)) +/// # .add_subkey(KeyFlags::empty().set_storage_encryption(), +/// # v, None) +/// # .add_subkey(KeyFlags::empty().set_signing(), +/// # v, None) +/// # .generate()?; +/// # assert_eq!(cert.keys().subkeys().count(), 2); +/// let vc = cert.with_policy(p, None)?; +/// # assert_eq!(vc.keys().subkeys().count(), 2); +/// # for ka in vc.keys().subkeys() { +/// # assert_eq!(ka.key_validity_period(), Some(v)); +/// # } +/// +/// // If you only want to extend non-expired keys, then add .alive(). +/// let packets = vc.keys().subkeys().revoked(false) +/// .map(|ka| { +/// SubkeyBuilder::from(ka) +/// .set_signature_creation_time(now)? +/// .set_key_expiration_time(e)? +/// .attach() +/// }) +/// .collect::<Result<Vec<Vec<Packet>>, _>>()?; +/// let cert = cert.insert_packets(packets.into_iter().flatten())?; +/// +/// let vc = cert.with_policy(p, now)?; +/// # assert_eq!(vc.keys().subkeys().count(), 2); +/// for ka in vc.keys().subkeys().revoked(false) { +/// // Check that the key's expiration time is really e. Note: We +/// // need to take into account that SystemTime has a subsecond +/// // resolution, but OpenPGP's timestamps only have a 1 second +/// // resolution. +/// assert!(e.duration_since(ka.key_expiration_time().unwrap()).unwrap() +/// < Duration::new(1, 0)); +/// } +/// # Ok(()) +/// # } +/// ``` +pub struct SubkeyBuilder<'a> { + vc: ValidCert<'a>, + primary_signer: Option<Box<dyn Signer + Send + Sync + 'a>>, + + subkey: Key<key::UnspecifiedParts, key::SubordinateRole>, + subkey_signer: Option<Box<dyn Signer + Send + Sync + 'a>>, + + template: SignatureBuilder, +} +assert_send_and_sync!(SubkeyBuilder<'_>); + +impl<'a> SubkeyBuilder<'a> { + /// Returns a SubkeyBuilder that will add the key to the specified + /// certificate. + /// + /// If the subkey is already present on the certificate, then the + /// `SubkeyBuilder` effectively adds a new binding signature to + /// the certificate. + pub fn new<P>(vc: ValidCert<'a>, + subkey: Key<P, key::SubordinateRole>, + subkey_flags: KeyFlags) + -> Result<Self> + where P: key::KeyParts, + { + // If the key is already present on the certificate, then we + // use the current self signature on that subkey as the + // template. + let (template, key_expiration): (SignatureBuilder, Option<SystemTime>) + = vc.keys().subkeys() + .filter_map(|ka| { + if ka.key().parts_as_unspecified().public_eq(&subkey) { + let sig = ka.binding_signature().clone(); + let e = sig.key_validity_period().map(|v| { + ka.key().creation_time() + v + }); + Some((sig.into(), e)) + } else { + None + } + }) + .next() + .or_else(|| { + // The key is completely new. Use the active self + // signature on the newest, non-revoked, non-expired + // subkey. + vc.keys().subkeys().revoked(false).alive() + // Fallback to sorting by fingerprint to ensure + // this is deterministic. + .max_by_key(|ka| (ka.key().creation_time(), ka.fingerprint())) + .map(|ka| { + let sig = ka.binding_signature().clone(); + let e = sig.key_validity_period().map(|v| { + ka.key().creation_time() + v + }); + (sig.into(), e) + }) + }) + .unwrap_or_else(|| { + // The certificate doesn't have any valid subkeys, so + // we don't have existing signatures that we can use + // as a template. In this case, we use a default + // binding signature, and the primary key's expiration + // time. + (SignatureBuilder::new(SignatureType::SubkeyBinding), + vc.primary_key().key_validity_period().map(|v| { + vc.primary_key().creation_time() + v + })) + }); + + let template = template.set_key_flags(subkey_flags)?; + + let mut builder = SubkeyBuilder { + vc, + primary_signer: None, + subkey: subkey.parts_into_unspecified(), + subkey_signer: None, + template: SignatureBuilder::new(SignatureType::SubkeyBinding), + }; + builder = builder.set_signature_template(template); + builder = builder.set_key_expiration_time(key_expiration)?; + + Ok(builder) + } + + /// Like SubkeyBuilder::new, but the binding signature is supplied. + /// + /// # Security Considerations + /// + /// The key validity period (i.e., the [Key Expiration Time + /// subpacket]) is left as is. **The Key Expiration Time + /// subpacket contains a relative time.** Thus, if you are using a + /// signature from another key with a different key creation time + /// as a template, the effective key expiration time will be + /// different! In this case, you should set the key expiration + /// time explicitly by calling + /// [`SubkeyBuilder::set_key_expiration_time`] or + /// [`SubkeyBuilder::set_key_validity_period`]. + /// + /// [Key Expiration Time subpacket]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.6 + /// + /// # Examples + /// + /// Adjusting the key expiration time: + /// + /// ``` + /// use std::time::{SystemTime, Duration}; + /// + /// use sequoia_openpgp as openpgp; + /// use openpgp::cert::prelude::*; + /// use openpgp::packet::Key; + /// use openpgp::packet::key::Key4; + /// use openpgp::policy::StandardPolicy; + /// use openpgp::types::KeyFlags; + /// + /// # fn main() -> openpgp::Result<()> { + /// let p = &StandardPolicy::new(); + /// + /// let now = SystemTime::now(); + /// let year = Duration::new(365 * 24 * 60 * 60, 0); + /// let last_year = now - year; + /// // cert was created last year and expires after two years. + /// let (cert, _) = + /// CertBuilder::new() + /// .set_creation_time(now - year) + /// .add_subkey(KeyFlags::empty().set_transport_encryption(), + /// 2 * year, None) + /// .generate()?; + /// + /// // Import a raw key and add it to the certificate. We + /// // explicitly reuse the existing subkey's signature, and adjust + /// // the key expiration time. + /// + /// # let q = b"\x57\x15\x45\x1B\x68\xA5\x13\xA2\x20\x0F\x71\x9D\xE3\x05\x3B\xED\xA2\x21\xDE\x61\x5A\xF5\x67\x45\xBB\x97\x99\x43\x53\x59\x7C\x3F"; + /// let k: Key<_, _> = Key4::import_public_ed25519(q, now)?.into(); + /// + /// let vc = cert.with_policy(p, now)?; + /// let template + /// = vc.keys().subkeys().next().unwrap().binding_signature().clone(); + /// # let vc1 = vc.clone(); + /// let cert2 = SubkeyBuilder::new_with(vc, k, template) + /// .set_key_validity_period(year)? + /// .attach_cert()?; + /// let vc2 = cert2.with_policy(p, now)?; + /// # assert_eq!(vc1.keys().count() + 1, vc2.keys().count()); + /// + /// // Observe that both keys expire one year from now. If we + /// // hadn't adjust the validity period of the new key, it would + /// // have expired in two years from now, because the key validity + /// // period is relative to the key's creation time! + /// vc2.keys().subkeys().for_each(|sig| { + /// // SystemTime has a subsection resolution. + /// assert!((now + year) + /// .duration_since(sig.key_expiration_time().unwrap()) + /// .unwrap() + /// < Duration::new(1, 0)); + /// }); + /// # Ok(()) + /// # } + /// ``` + pub fn new_with<P, T>(vc: ValidCert<'a>, + subkey: Key<P, key::SubordinateRole>, + template: T) + -> Self + where P: key::KeyParts, + T: Into<SignatureBuilder>, + { + let template = template.into(); + + let mut builder = SubkeyBuilder { + vc, + primary_signer: None, + subkey: subkey.parts_into_unspecified(), + subkey_signer: None, + template: SignatureBuilder::new(SignatureType::SubkeyBinding), + }; + builder = builder.set_signature_template(template); + builder + } + + /// Sets the signature template that will be used for the binding + /// signature. + /// + /// This effectively discards any previous calls to + /// [`SubkeyBuilder::set_signature_creation_time`], + /// [`SubkeyBuilder::set_key_expiration_time`], etc. + /// + /// This function modifies the template as follows: + /// + /// - The hash algorithm is set to a safe default. + /// + /// These changes can be overridden by using + /// [`SubkeyBuilder::with_signature_template`]. + /// + /// # Security Considerations + /// + /// The key validity period (i.e., the [Key Expiration Time + /// subpacket]) is left as is. **This packet contains a relative + /// time.** Thus, if you are using a Signature from another key + /// with a different key creation time as a template, the + /// effective key expiration time will be different! In this + /// case, you should set the key expiration time explicitly by + /// calling [`SubkeyBuilder::set_key_expiration_time`] or + /// [`SubkeyBuilder::set_key_validity_period`]. + /// + /// [Key Expiration Time subpacket]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.3.6 + pub fn set_signature_template<T>(mut self, template: T) -> Self + where T: Into<SignatureBuilder>, + { + self.template = template.into(); + + // GnuPG wants at least a 512-bit hash for P521 keys. + self.template = self.template.set_hash_algo(HashAlgorithm::SHA512); + + self + } + + /// Allows a function to directly modify the signature template. + /// + /// This function does not fail; it returns the result of the + /// callback function. + /// + /// # Examples + /// + /// Add a notation to an existing key: + /// + /// ``` + /// use sequoia_openpgp as openpgp; + /// use openpgp::cert::prelude::*; + /// use openpgp::packet::signature::subpacket::NotationDataFlags; + /// use openpgp::policy::StandardPolicy; + /// use openpgp::types::KeyFlags; + /// + /// # fn main() -> openpgp::Result<()> { + /// let p = &StandardPolicy::new(); + /// + /// # let (cert, _) = CertBuilder::new().add_signing_subkey().generate()?; + /// let vc = cert.with_policy(p, None)?; + /// let cert2 = SubkeyBuilder::from(vc.keys().subkeys().next().unwrap()) + /// .with_signature_template(|sig| { + /// sig.add_notation("policy@example.org", b"1", + /// NotationDataFlags::empty().set_human_readable(), + /// false /* critical */) + /// })? + /// .attach_cert()?; + /// # let vc2 = cert2.with_policy(p, None)?; + /// # assert_eq!(vc2.keys().count(), 2); + /// # let ka = vc2.keys().subkeys().next().unwrap(); + /// # assert_eq!(ka.self_signatures().count(), 2); + /// # assert_eq!( + /// # ka.binding_signature().notation("policy@example.org") + /// # .collect::<Vec<_>>(), + /// # vec![ b"1" ]); + /// # Ok(()) + /// # } + /// ``` + pub fn with_signature_template<F>(mut self, f: F) -> Result<Self> + where F: FnOnce(SignatureBuilder) -> Result<SignatureBuilder> + { + self.template = f(self.template.clone())?; + + Ok(self) + } + + /// Sets the binding signature's creation time. + /// + /// This directly modifies the current signature template. + /// + /// This just calls + /// [`SignatureBuilder::set_signature_creation_time`] on the + /// signature template. + pub fn set_signature_creation_time<T>(mut self, creation_time: T) + -> Result<Self> + where T: Into<SystemTime> + { + self.template = self.template.set_signature_creation_time( |