summaryrefslogtreecommitdiffstats
path: root/openpgp
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2022-03-09 11:09:25 +0100
committerNeal H. Walfield <neal@pep.foundation>2022-06-14 10:58:57 +0200
commit36270e24d2761a40e92668ab0d083afae322da4c (patch)
tree9097180fcd418fb047cd5f1429eaf3377d02c885 /openpgp
parent429887d57f705e81e305008cfc95162432d7e684 (diff)
openpgp: Add a subkey builder.
- Add `KeyBuilder` and `SubkeyBuilder` for creating a key, and attaching a subkey to a certificate. - See #483.
Diffstat (limited to 'openpgp')
-rw-r--r--openpgp/NEWS2
-rw-r--r--openpgp/src/cert.rs2
-rw-r--r--openpgp/src/cert/builder.rs16
-rw-r--r--openpgp/src/cert/builder/key.rs1015
-rw-r--r--openpgp/src/cert/prelude.rs2
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_temp