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:

The following information is also meaningful in the context of a subkey binding signature:

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>

source

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.

source

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));
});
source

pub fn set_signature_template<T>(self, template: T) -> Self

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.

source

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()?;
source

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.

source

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.

source

pub fn set_key_expiration_time<T>(self, key_expiration_time: T) -> Result<Self>
where T: Into<Option<SystemTime>>,

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.

source

pub fn set_key_validity_period<T>(self, validity: T) -> Result<Self>
where T: Into<Option<Duration>>,

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.

source

pub fn key(&self) -> &Key<UnspecifiedParts, SubordinateRole>

Returns a reference to the subkey.

source

pub fn set_primary_key_signer<S>(self, signer: S) -> Self
where S: Signer + Send + Sync + 'a,

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.

source

pub fn set_subkey_signer<S>(self, signer: S) -> Self
where S: Signer + Send + Sync + 'a,

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.

source

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.

source

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()?;

Trait Implementations§

source§

impl<'a, P> From<ValidKeyAmalgamation<'a, P, PrimaryRole, ()>> for SubkeyBuilder<'a>
where P: KeyParts + Clone,

source§

fn from(ka: ValidPrimaryKeyAmalgamation<'a, P>) -> Self

Converts to this type from the input type.
source§

impl<'a, P> From<ValidKeyAmalgamation<'a, P, SubordinateRole, ()>> for SubkeyBuilder<'a>
where P: KeyParts + Clone,

source§

fn from(ka: ValidSubordinateKeyAmalgamation<'a, P>) -> Self

Converts to this type from the input type.
source§

impl<'a, P> From<ValidKeyAmalgamation<'a, P, UnspecifiedRole, bool>> for SubkeyBuilder<'a>
where P: KeyParts + Clone,

source§

fn from(ka: ValidErasedKeyAmalgamation<'a, P>) -> SubkeyBuilder<'a>

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<'a> !Freeze for SubkeyBuilder<'a>

§

impl<'a> !RefUnwindSafe for SubkeyBuilder<'a>

§

impl<'a> Send for SubkeyBuilder<'a>

§

impl<'a> Sync for SubkeyBuilder<'a>

§

impl<'a> Unpin for SubkeyBuilder<'a>

§

impl<'a> !UnwindSafe for SubkeyBuilder<'a>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> ErasedDestructor for T
where T: 'static,

source§

impl<T> MaybeSendSync for T