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, creation_time: Option, } 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 { 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(mut self, creation_time: T) -> Self where T: Into> { 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(mut self, password: T) -> Self where T: Into> { 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, empirically it seems that some virtual /// machines have laggy clocks. pub fn subkey(self, vc: ValidCert) -> Result> { let mut key: Key = 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::>, _>>()?; /// 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>, subkey: Key, subkey_signer: Option>, 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

(vc: ValidCert<'a>, subkey: Key, subkey_flags: KeyFlags) -> Result 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) = 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(vc: ValidCert<'a>, subkey: Key, template: T) -> Self where P: key::KeyParts, T: Into, { 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(mut self, template: T) -> Self where T: Into, { 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![ b"1" ]); /// # Ok(()) /// # } /// ``` pub fn with_signature_template(mut self, f: F) -> Result where F: FnOnce(SignatureBuilder) -> Result { 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(mut self, creation_time: T) -> Result where T: Into { self.template = self.template.set_signature_creation_time( creation_time.into())?; Ok(self) } /// Preserves the signature creation time set in the template. /// /// This directly modifies the current signature template. /// /// This just calls /// [`SignatureBuilder::preserve_signature_creation_time`] on the /// signature template. pub fn preserve_signature_creation_time(mut self) -> Result { self.template = self.template.preserve_signature_creation_time()?; Ok(self) } /// Sets the key's expiration time. /// /// This directly modifies the current signature template. /// /// This returns an error if the expiration time is before the /// key's creation time. pub fn set_key_expiration_time(mut self, key_expiration_time: T) -> Result where T: Into> { let key_expiration_time = key_expiration_time.into(); let validity_period = key_expiration_time .map(|e| { e.duration_since(self.subkey.creation_time()) .map_err(|_| { Error::InvalidArgument( "expiration time precedes creation time".into()) }) }) .transpose()?; self = self.with_signature_template(|sig| { sig.set_key_validity_period(validity_period) })?; Ok(self) } /// Sets the key's validity period. /// /// The validity period is the amount of time after the key's /// creation time that the key is considered fresh (i.e., not /// expired). /// /// This directly modifies the current signature template. pub fn set_key_validity_period(mut self, validity: T) -> Result where T: Into> { self = self.with_signature_template(|sig| { sig.set_key_validity_period(validity.into()) })?; Ok(self) } /// Returns a reference to the subkey. pub fn key(&self) -> &Key { &self.subkey } /// Adds a signer for the primary key. /// /// In order to attach a subkey to a certificate one or more /// signatures need to be issued. First, the primary key needs to /// issue a [subkey binding signature]. If the subkey is signing /// capable, then it also needs to issue a [primary key binding /// signature]. By default, [`SubkeyBuilder::attach`] will /// automatically derive the signers from the key material. This /// only works, however, if the key material is present, and it is /// unencrypted. This method allows you to explicitly provide a /// signer for the primary key. /// /// [subkey binding signature]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.1 /// [primary binding signature]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.1 pub fn set_primary_key_signer(mut self, signer: S) -> Self where S: Signer + Send + Sync + 'a, { self.primary_signer = Some(Box::new(signer)); self } /// Adds a signer for the subkey. /// /// In order to attach a subkey to a certificate one or more /// signatures need to be issued. First, the primary key needs to /// issue a [subkey binding signature]. If the subkey is signing /// capable, then it also needs to issue a [primary key binding /// signature]. By default, [`SubkeyBuilder::attach`] will /// automatically derive the signers from the key material. This /// only works, however, if the key material is present, and it is /// unencrypted. This method allows you to explicitly provide a /// signer for the subkey. /// /// [subkey binding signature]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.1 /// [primary binding signature]: https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.1 pub fn set_subkey_signer(mut self, signer: S) -> Self where S: Signer + Send + Sync + 'a, { self.subkey_signer = Some(Box::new(signer)); self } /// Attaches the subkey to the certificate. /// /// This method generates the appropriate signatures to attach the /// subkey to the certificate. /// /// This function returns an error if the expiration time would /// cause the key to expire before the binding signature's /// expiration time. /// /// This method returns a number of packets, which need to be /// merged into the cert. This can be done using /// [`Cert::insert_packets`]. pub fn attach(self) -> Result> { let SubkeyBuilder { vc, primary_signer, subkey, subkey_signer, template, } = self; if template.typ() != SignatureType::SubkeyBinding { return Err(Error::InvalidArgument( format!("Expected a SubkeyBinding signature, got a {}", template.typ())).into()); } let mut builder = template; let creation_time = builder.effective_signature_creation_time()?; // creation_time is only None if // preserve_signature_creation_time is done and that's a // Highly Advanced Interface that doesn't need sanity checks. if let Some(sig_ct) = creation_time { if let Some(v) = builder.key_validity_period() { let e = subkey.creation_time() + v; if let Err(err) = e.duration_since(sig_ct) { return Err(Error::InvalidArgument( format!( "key expiration precedes signature creation time \ (by {:?})", err.duration())).into()); } } } if let Some(flags) = builder.key_flags() { if flags.for_certification() || flags.for_signing() { // We need to create a primary key binding signature. let mut subkey_signer = if let Some(signer) = subkey_signer { signer } else { Box::new( subkey.clone().parts_into_secret()?.into_keypair()?) }; let mut backsig = SignatureBuilder::new( SignatureType::PrimaryKeyBinding) // GnuPG wants at least a 512-bit hash for P521 keys. .set_hash_algo(HashAlgorithm::SHA512) .set_reference_time(creation_time); if let Some(t) = creation_time { backsig = backsig.set_reference_time(t); } else { backsig = backsig.preserve_signature_creation_time()?; } let backsig = backsig.sign_primary_key_binding( &mut *subkey_signer, &vc.primary_key(), &subkey)?; builder = builder.set_embedded_signature(backsig)?; } else { // We don't need the embedded signature, remove it. builder.hashed_area_mut() .remove_all(SubpacketTag::EmbeddedSignature); builder.unhashed_area_mut() .remove_all(SubpacketTag::EmbeddedSignature); } } let mut primary_signer = if let Some(signer) = primary_signer { signer } else { Box::new( vc.primary_key().key().clone() .parts_into_secret()?.into_keypair()?) }; let signature = subkey.bind( &mut *primary_signer, vc.cert(), builder)?; let subkey = if subkey.has_secret() { Packet::SecretSubkey(subkey.parts_into_secret().unwrap()) } else { Packet::PublicSubkey(subkey.parts_into_public()) }; Ok(vec![subkey, signature.into()]) } /// Attaches the subkey directly to the certificate. /// /// This function is like [`SubkeyBuilder::attach`], but it also /// merges the resulting packets into the certificate. /// /// Note: if you are adding multiple subkeys to a certificate or /// updating multiple subkeys, it is usually more efficient to use /// [`SubkeyBuilder::attach`], and then merge all of the packets /// at once. /// /// # Examples /// /// ``` /// 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 cert2 = KeyBuilder::new(KeyFlags::empty().set_signing()) /// .subkey(vc)? /// .attach_cert()?; /// # let vc2 = cert2.with_policy(p, None)?; /// # assert_eq!(vc1.keys().count() + 1, vc2.keys().count()); /// # Ok(()) /// # } /// ``` pub fn attach_cert(self) -> Result { let cert = self.vc.cert().clone(); let packets = self.attach()?; Ok(cert.insert_packets(packets)?) } } impl<'a, P> From> for SubkeyBuilder<'a> where P: key::KeyParts + Clone, { fn from(ka: ValidPrimaryKeyAmalgamation<'a, P>) -> Self { ValidErasedKeyAmalgamation::from(ka).into() } } impl<'a, P> From> for SubkeyBuilder<'a> where P: key::KeyParts + Clone, { fn from(ka: ValidSubordinateKeyAmalgamation<'a, P>) -> Self { ValidErasedKeyAmalgamation::from(ka).into() } } impl<'a, P> From> for SubkeyBuilder<'a> where P: key::KeyParts + Clone, { fn from(ka: ValidErasedKeyAmalgamation<'a, P>) -> SubkeyBuilder<'a> { let key = ka.key().clone().role_into_subordinate(); SubkeyBuilder::new_with( ka.cert().clone(), key, ka.binding_signature().clone()) } } #[cfg(test)] mod test { use super::*; use std::time::{Duration, UNIX_EPOCH}; use crate::policy::StandardPolicy; #[test] fn expiry() -> Result<()> { let p = &StandardPolicy::new(); // t0: Create certificate, keys expire at t2. // t1: Add a new key, heuristic should have it expire at t2. // t2: All keys expire. // We do it all in the past to make sure the current time is // never used. // Avoid milliseconds. let t1 = crate::now() - Duration::new(7 * 24 * 60 * 60, 0); let t1 = t1.duration_since(UNIX_EPOCH)?.as_secs(); let t1 = UNIX_EPOCH + Duration::new(t1, 0); let t0 = t1 - Duration::new(60 * 60, 0); let t2 = t1 + Duration::new(60 * 60, 0); let validity = t2.duration_since(t0).unwrap(); let (pre, _) = CertBuilder::general_purpose(None, Some("alice@example.org")) .set_creation_time(t0) .set_validity_period(validity) .generate()?; let vc_pre = pre.with_policy(p, t1)?; let post = KeyBuilder::new(KeyFlags::empty().set_signing()) .set_creation_time(t1) .subkey(vc_pre)? .set_signature_creation_time(t1)? .attach_cert()?; let vc_post = post.with_policy(p, t1).unwrap(); // We should have one more key. assert_eq!(pre.keys().count() + 1, post.keys().count()); // Make sure the signature and backsig are valid. assert_eq!(post.keys().count(), vc_post.keys().count()); // And the new key should have inherited the other keys' // expiration. eprintln!("t0: {:?}", t0); eprintln!("t1: {:?}", t1); eprintln!("t2: {:?}", t2); assert!(vc_post.keys().all(|ka| { eprintln!("{}: {:?} -> {:?}", ka.fingerprint(), ka.creation_time(), ka.key_expiration_time()); ka.key_expiration_time() == Some(t2) })); Ok(()) } }