Struct sequoia_openpgp::cert::SubkeyBuilder
source · pub struct SubkeyBuilder<'a> { /* private fields */ }
Expand description
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:
-
Issuer and Issuer Fingerprint.
-
Primary key binding signature (if the key is signing capable)
The following information is also meaningful in the context of a subkey binding signature:
-
Key expiration time (relative to the key’s creation time, not the signature’s creation time!)
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;
let p = &StandardPolicy::new();
let vc = cert.with_policy(p, None)?;
let cert_new = KeyBuilder::new(KeyFlags::empty().set_signing())
.subkey(vc)?
.attach_cert()?;
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;
let p = &StandardPolicy::new();
let k: Key<_, _>
= Key4::import_public_ed25519(q, SystemTime::now())?.into();
let vc = cert.with_policy(p, None)?;
let mut cert2 = SubkeyBuilder::new(
vc, k.parts_into_unspecified(),
KeyFlags::empty().set_transport_encryption())?
.attach_cert()?;
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;
let p = &StandardPolicy::new();
let now = SystemTime::now();
let e = now + Duration::new(365 * 24 * 60 * 60, 0);
let vc = cert.with_policy(p, None)?;
// 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)?;
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));
}
Implementations§
source§impl<'a> SubkeyBuilder<'a>
impl<'a> SubkeyBuilder<'a>
sourcepub fn new<P>(
vc: ValidCert<'a>,
subkey: Key<P, SubordinateRole>,
subkey_flags: KeyFlags,
) -> Result<Self>where
P: KeyParts,
pub fn new<P>(
vc: ValidCert<'a>,
subkey: Key<P, SubordinateRole>,
subkey_flags: KeyFlags,
) -> Result<Self>where
P: KeyParts,
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.
sourcepub fn new_with<P, T>(
vc: ValidCert<'a>,
subkey: Key<P, SubordinateRole>,
template: T,
) -> Self
pub fn new_with<P, T>( vc: ValidCert<'a>, subkey: Key<P, SubordinateRole>, template: T, ) -> Self
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
.
§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;
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 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 cert2 = SubkeyBuilder::new_with(vc, k, template)
.set_key_validity_period(year)?
.attach_cert()?;
let vc2 = cert2.with_policy(p, now)?;
// 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));
});
sourcepub fn set_signature_template<T>(self, template: T) -> Selfwhere
T: Into<SignatureBuilder>,
pub fn set_signature_template<T>(self, template: T) -> Selfwhere
T: Into<SignatureBuilder>,
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
.
sourcepub fn with_signature_template<F>(self, f: F) -> Result<Self>
pub fn with_signature_template<F>(self, f: F) -> Result<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;
let p = &StandardPolicy::new();
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()?;
sourcepub fn set_signature_creation_time<T>(self, creation_time: T) -> Result<Self>where
T: Into<SystemTime>,
pub fn set_signature_creation_time<T>(self, creation_time: T) -> Result<Self>where
T: Into<SystemTime>,
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.
sourcepub fn preserve_signature_creation_time(self) -> Result<Self>
pub fn preserve_signature_creation_time(self) -> Result<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.
sourcepub fn set_key_expiration_time<T>(self, key_expiration_time: T) -> Result<Self>
pub fn set_key_expiration_time<T>(self, key_expiration_time: T) -> Result<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.
sourcepub fn set_key_validity_period<T>(self, validity: T) -> Result<Self>
pub fn set_key_validity_period<T>(self, validity: T) -> Result<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.
sourcepub fn key(&self) -> &Key<UnspecifiedParts, SubordinateRole>
pub fn key(&self) -> &Key<UnspecifiedParts, SubordinateRole>
Returns a reference to the subkey.
sourcepub fn set_primary_key_signer<S>(self, signer: S) -> Self
pub fn set_primary_key_signer<S>(self, signer: S) -> Self
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.
sourcepub fn set_subkey_signer<S>(self, signer: S) -> Self
pub fn set_subkey_signer<S>(self, signer: S) -> 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.
sourcepub fn attach(self) -> Result<Vec<Packet>>
pub fn attach(self) -> Result<Vec<Packet>>
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
.
sourcepub fn attach_cert(self) -> Result<Cert>
pub fn attach_cert(self) -> Result<Cert>
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;
let p = &StandardPolicy::new();
let vc = cert.with_policy(p, None)?;
let cert2 = KeyBuilder::new(KeyFlags::empty().set_signing())
.subkey(vc)?
.attach_cert()?;