use std::time;
use std::time::SystemTime;
use std::ops::Deref;
use std::borrow::Borrow;
use std::convert::TryFrom;
use std::convert::TryInto;
use anyhow::Context;
use crate::{
Cert,
cert::bundle::KeyBundle,
cert::amalgamation::{
ComponentAmalgamation,
ValidAmalgamation,
ValidateAmalgamation,
},
cert::ValidCert,
crypto::Signer,
Error,
packet::Key,
packet::key,
packet::Signature,
packet::signature,
packet::signature::subpacket::SubpacketTag,
policy::Policy,
Result,
seal,
types::{
KeyFlags,
RevocationStatus,
SignatureType,
},
};
mod iter;
pub use iter::{
KeyAmalgamationIter,
ValidKeyAmalgamationIter,
};
pub trait PrimaryKey<'a, P, R>: seal::Sealed
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
{
fn primary(&self) -> bool;
}
#[derive(Debug)]
pub struct KeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
{
ca: ComponentAmalgamation<'a, Key<P, R>>,
primary: R2,
}
assert_send_and_sync!(KeyAmalgamation<'_, P, R, R2>
where P: key::KeyParts,
R: key::KeyRole,
R2,
);
impl<'a, P, R, R2> Clone for KeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
{
fn clone(&self) -> Self {
Self {
ca: self.ca.clone(),
primary: self.primary,
}
}
}
pub type PrimaryKeyAmalgamation<'a, P>
= KeyAmalgamation<'a, P, key::PrimaryRole, ()>;
pub type SubordinateKeyAmalgamation<'a, P>
= KeyAmalgamation<'a, P, key::SubordinateRole, ()>;
pub type ErasedKeyAmalgamation<'a, P>
= KeyAmalgamation<'a, P, key::UnspecifiedRole, bool>;
impl<'a, P, R, R2> Deref for KeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
{
type Target = ComponentAmalgamation<'a, Key<P, R>>;
fn deref(&self) -> &Self::Target {
&self.ca
}
}
impl<'a, P> seal::Sealed
for PrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::PrimaryRole>>
for PrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = ValidPrimaryKeyAmalgamation<'a, P>;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
-> Result<Self::V>
where T: Into<Option<time::SystemTime>>
{
let ka : ErasedKeyAmalgamation<P> = self.into();
Ok(ka.with_policy(policy, time)?
.try_into().expect("conversion is symmetric"))
}
}
impl<'a, P> seal::Sealed
for SubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::SubordinateRole>>
for SubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = ValidSubordinateKeyAmalgamation<'a, P>;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
-> Result<Self::V>
where T: Into<Option<time::SystemTime>>
{
let ka : ErasedKeyAmalgamation<P> = self.into();
Ok(ka.with_policy(policy, time)?
.try_into().expect("conversion is symmetric"))
}
}
impl<'a, P> seal::Sealed
for ErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::UnspecifiedRole>>
for ErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = ValidErasedKeyAmalgamation<'a, P>;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
-> Result<Self::V>
where T: Into<Option<time::SystemTime>>
{
let time = time.into().unwrap_or_else(SystemTime::now);
if ! self.primary() {
let pka = PrimaryKeyAmalgamation::new(self.cert());
pka.with_policy(policy, time).context("primary key")?;
}
let binding_signature = self.binding_signature(policy, time)?;
let cert = self.ca.cert();
let vka = ValidErasedKeyAmalgamation {
ka: KeyAmalgamation {
ca: self.ca.parts_into_public(),
primary: self.primary,
},
cert: ValidCert {
cert: cert,
policy: policy,
time: time,
},
binding_signature
};
policy.key(&vka)?;
Ok(ValidErasedKeyAmalgamation {
ka: KeyAmalgamation {
ca: P::convert_key_amalgamation(
vka.ka.ca.parts_into_unspecified()).expect("roundtrip"),
primary: vka.ka.primary,
},
cert: vka.cert,
binding_signature,
})
}
}
impl<'a, P> PrimaryKey<'a, P, key::PrimaryRole>
for PrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
true
}
}
impl<'a, P> PrimaryKey<'a, P, key::SubordinateRole>
for SubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
false
}
}
impl<'a, P> PrimaryKey<'a, P, key::UnspecifiedRole>
for ErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
self.primary
}
}
impl<'a, P: 'a + key::KeyParts> From<PrimaryKeyAmalgamation<'a, P>>
for ErasedKeyAmalgamation<'a, P>
{
fn from(ka: PrimaryKeyAmalgamation<'a, P>) -> Self {
ErasedKeyAmalgamation {
ca: ka.ca.role_into_unspecified(),
primary: true,
}
}
}
impl<'a, P: 'a + key::KeyParts> From<SubordinateKeyAmalgamation<'a, P>>
for ErasedKeyAmalgamation<'a, P>
{
fn from(ka: SubordinateKeyAmalgamation<'a, P>) -> Self {
ErasedKeyAmalgamation {
ca: ka.ca.role_into_unspecified(),
primary: false,
}
}
}
macro_rules! impl_conversion {
($s:ident, $primary:expr, $p1:path, $p2:path) => {
impl<'a> From<$s<'a, $p1>>
for ErasedKeyAmalgamation<'a, $p2>
{
fn from(ka: $s<'a, $p1>) -> Self {
ErasedKeyAmalgamation {
ca: ka.ca.into(),
primary: $primary,
}
}
}
}
}
impl_conversion!(PrimaryKeyAmalgamation, true,
key::SecretParts, key::PublicParts);
impl_conversion!(PrimaryKeyAmalgamation, true,
key::SecretParts, key::UnspecifiedParts);
impl_conversion!(PrimaryKeyAmalgamation, true,
key::PublicParts, key::UnspecifiedParts);
impl_conversion!(PrimaryKeyAmalgamation, true,
key::UnspecifiedParts, key::PublicParts);
impl_conversion!(SubordinateKeyAmalgamation, false,
key::SecretParts, key::PublicParts);
impl_conversion!(SubordinateKeyAmalgamation, false,
key::SecretParts, key::UnspecifiedParts);
impl_conversion!(SubordinateKeyAmalgamation, false,
key::PublicParts, key::UnspecifiedParts);
impl_conversion!(SubordinateKeyAmalgamation, false,
key::UnspecifiedParts, key::PublicParts);
impl<'a, P, P2> TryFrom<ErasedKeyAmalgamation<'a, P>>
for PrimaryKeyAmalgamation<'a, P2>
where P: 'a + key::KeyParts,
P2: 'a + key::KeyParts,
{
type Error = anyhow::Error;
fn try_from(ka: ErasedKeyAmalgamation<'a, P>) -> Result<Self> {
if ka.primary {
Ok(Self {
ca: P2::convert_key_amalgamation(
ka.ca.role_into_primary().parts_into_unspecified())?,
primary: (),
})
} else {
Err(Error::InvalidArgument(
"can't convert a SubordinateKeyAmalgamation \
to a PrimaryKeyAmalgamation".into()).into())
}
}
}
impl<'a, P, P2> TryFrom<ErasedKeyAmalgamation<'a, P>>
for SubordinateKeyAmalgamation<'a, P2>
where P: 'a + key::KeyParts,
P2: 'a + key::KeyParts,
{
type Error = anyhow::Error;
fn try_from(ka: ErasedKeyAmalgamation<'a, P>) -> Result<Self> {
if ka.primary {
Err(Error::InvalidArgument(
"can't convert a PrimaryKeyAmalgamation \
to a SubordinateKeyAmalgamation".into()).into())
} else {
Ok(Self {
ca: P2::convert_key_amalgamation(
ka.ca.role_into_subordinate().parts_into_unspecified())?,
primary: (),
})
}
}
}
impl<'a> PrimaryKeyAmalgamation<'a, key::PublicParts> {
pub(crate) fn new(cert: &'a Cert) -> Self {
PrimaryKeyAmalgamation {
ca: ComponentAmalgamation::new(cert, &cert.primary),
primary: (),
}
}
}
impl<'a, P: 'a + key::KeyParts> SubordinateKeyAmalgamation<'a, P> {
pub(crate) fn new(
cert: &'a Cert, bundle: &'a KeyBundle<P, key::SubordinateRole>)
-> Self
{
SubordinateKeyAmalgamation {
ca: ComponentAmalgamation::new(cert, bundle),
primary: (),
}
}
}
impl<'a, P: 'a + key::KeyParts> ErasedKeyAmalgamation<'a, P> {
fn binding_signature<T>(&self, policy: &'a dyn Policy, time: T)
-> Result<&'a Signature>
where T: Into<Option<time::SystemTime>>
{
let time = time.into().unwrap_or_else(SystemTime::now);
if self.primary {
self.cert().primary_userid_relaxed(policy, time, false)
.map(|u| u.binding_signature())
.or_else(|e0| {
self.cert().primary_key().bundle()
.binding_signature(policy, time)
.or_else(|e1| {
if let Some(Error::NoBindingSignature(_))
= e1.downcast_ref()
{
Err(e0) } else {
Err(e1)
}
})
})
} else {
self.bundle().binding_signature(policy, time)
}
}
}
impl<'a, P, R, R2> KeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
{
pub fn component_amalgamation(&self)
-> &ComponentAmalgamation<'a, Key<P, R>> {
&self.ca
}
pub fn key(&self) -> &'a Key<P, R> {
self.ca.component()
}
}
#[derive(Debug, Clone)]
pub struct ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
{
ka: KeyAmalgamation<'a, P, R, R2>,
cert: ValidCert<'a>,
binding_signature: &'a Signature,
}
assert_send_and_sync!(ValidKeyAmalgamation<'_, P, R, R2>
where P: key::KeyParts,
R: key::KeyRole,
R2: Copy,
);
pub type ValidPrimaryKeyAmalgamation<'a, P>
= ValidKeyAmalgamation<'a, P, key::PrimaryRole, ()>;
pub type ValidSubordinateKeyAmalgamation<'a, P>
= ValidKeyAmalgamation<'a, P, key::SubordinateRole, ()>;
pub type ValidErasedKeyAmalgamation<'a, P>
= ValidKeyAmalgamation<'a, P, key::UnspecifiedRole, bool>;
impl<'a, P, R, R2> Deref for ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
{
type Target = KeyAmalgamation<'a, P, R, R2>;
fn deref(&self) -> &Self::Target {
&self.ka
}
}
impl<'a, P, R, R2> From<ValidKeyAmalgamation<'a, P, R, R2>>
for KeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
{
fn from(vka: ValidKeyAmalgamation<'a, P, R, R2>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
vka.ka
}
}
impl<'a, P: 'a + key::KeyParts> From<ValidPrimaryKeyAmalgamation<'a, P>>
for ValidErasedKeyAmalgamation<'a, P>
{
fn from(vka: ValidPrimaryKeyAmalgamation<'a, P>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.into(),
cert: vka.cert,
binding_signature: vka.binding_signature,
}
}
}
impl<'a, P: 'a + key::KeyParts> From<&ValidPrimaryKeyAmalgamation<'a, P>>
for ValidErasedKeyAmalgamation<'a, P>
{
fn from(vka: &ValidPrimaryKeyAmalgamation<'a, P>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.clone().into(),
cert: vka.cert.clone(),
binding_signature: vka.binding_signature,
}
}
}
impl<'a, P: 'a + key::KeyParts> From<ValidSubordinateKeyAmalgamation<'a, P>>
for ValidErasedKeyAmalgamation<'a, P>
{
fn from(vka: ValidSubordinateKeyAmalgamation<'a, P>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.into(),
cert: vka.cert,
binding_signature: vka.binding_signature,
}
}
}
impl<'a, P: 'a + key::KeyParts> From<&ValidSubordinateKeyAmalgamation<'a, P>>
for ValidErasedKeyAmalgamation<'a, P>
{
fn from(vka: &ValidSubordinateKeyAmalgamation<'a, P>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.clone().into(),
cert: vka.cert.clone(),
binding_signature: vka.binding_signature,
}
}
}
macro_rules! impl_conversion {
($s:ident, $p1:path, $p2:path) => {
impl<'a> From<$s<'a, $p1>>
for ValidErasedKeyAmalgamation<'a, $p2>
{
fn from(vka: $s<'a, $p1>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.into(),
cert: vka.cert,
binding_signature: vka.binding_signature,
}
}
}
impl<'a> From<&$s<'a, $p1>>
for ValidErasedKeyAmalgamation<'a, $p2>
{
fn from(vka: &$s<'a, $p1>) -> Self {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
ValidErasedKeyAmalgamation {
ka: vka.ka.clone().into(),
cert: vka.cert.clone(),
binding_signature: vka.binding_signature,
}
}
}
}
}
impl_conversion!(ValidPrimaryKeyAmalgamation,
key::SecretParts, key::PublicParts);
impl_conversion!(ValidPrimaryKeyAmalgamation,
key::SecretParts, key::UnspecifiedParts);
impl_conversion!(ValidPrimaryKeyAmalgamation,
key::PublicParts, key::UnspecifiedParts);
impl_conversion!(ValidPrimaryKeyAmalgamation,
key::UnspecifiedParts, key::PublicParts);
impl_conversion!(ValidSubordinateKeyAmalgamation,
key::SecretParts, key::PublicParts);
impl_conversion!(ValidSubordinateKeyAmalgamation,
key::SecretParts, key::UnspecifiedParts);
impl_conversion!(ValidSubordinateKeyAmalgamation,
key::PublicParts, key::UnspecifiedParts);
impl_conversion!(ValidSubordinateKeyAmalgamation,
key::UnspecifiedParts, key::PublicParts);
impl<'a, P, P2> TryFrom<ValidErasedKeyAmalgamation<'a, P>>
for ValidPrimaryKeyAmalgamation<'a, P2>
where P: 'a + key::KeyParts,
P2: 'a + key::KeyParts,
{
type Error = anyhow::Error;
fn try_from(vka: ValidErasedKeyAmalgamation<'a, P>) -> Result<Self> {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
Ok(ValidPrimaryKeyAmalgamation {
ka: vka.ka.try_into()?,
cert: vka.cert,
binding_signature: vka.binding_signature,
})
}
}
impl<'a, P, P2> TryFrom<ValidErasedKeyAmalgamation<'a, P>>
for ValidSubordinateKeyAmalgamation<'a, P2>
where P: 'a + key::KeyParts,
P2: 'a + key::KeyParts,
{
type Error = anyhow::Error;
fn try_from(vka: ValidErasedKeyAmalgamation<'a, P>) -> Result<Self> {
Ok(ValidSubordinateKeyAmalgamation {
ka: vka.ka.try_into()?,
cert: vka.cert,
binding_signature: vka.binding_signature,
})
}
}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::PrimaryRole>>
for ValidPrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = Self;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized
{
assert!(std::ptr::eq(self.ka.cert(), self.cert.cert()));
self.ka.with_policy(policy, time)
.map(|vka| {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
vka
})
}
}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::SubordinateRole>>
for ValidSubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = Self;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized
{
assert!(std::ptr::eq(self.ka.cert(), self.cert.cert()));
self.ka.with_policy(policy, time)
.map(|vka| {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
vka
})
}
}
impl<'a, P> ValidateAmalgamation<'a, Key<P, key::UnspecifiedRole>>
for ValidErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
type V = Self;
fn with_policy<T>(self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized
{
assert!(std::ptr::eq(self.ka.cert(), self.cert.cert()));
self.ka.with_policy(policy, time)
.map(|vka| {
assert!(std::ptr::eq(vka.ka.cert(), vka.cert.cert()));
vka
})
}
}
impl<'a, P, R, R2> seal::Sealed for ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
Self: PrimaryKey<'a, P, R>,
{}
impl<'a, P, R, R2> ValidAmalgamation<'a, Key<P, R>>
for ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
Self: PrimaryKey<'a, P, R>,
{
fn cert(&self) -> &ValidCert<'a> {
assert!(std::ptr::eq(self.ka.cert(), self.cert.cert()));
&self.cert
}
fn time(&self) -> SystemTime {
self.cert.time()
}
fn policy(&self) -> &'a dyn Policy {
assert!(std::ptr::eq(self.ka.cert(), self.cert.cert()));
self.cert.policy()
}
fn binding_signature(&self) -> &'a Signature {
self.binding_signature
}
fn revocation_status(&self) -> RevocationStatus<'a> {
if self.primary() {
self.cert.revocation_status()
} else {
self.bundle()._revocation_status(self.policy(), self.time(),
true, Some(self.binding_signature))
}
}
}
impl<'a, P> PrimaryKey<'a, P, key::PrimaryRole>
for ValidPrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
true
}
}
impl<'a, P> PrimaryKey<'a, P, key::SubordinateRole>
for ValidSubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
false
}
}
impl<'a, P> PrimaryKey<'a, P, key::UnspecifiedRole>
for ValidErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
fn primary(&self) -> bool {
self.ka.primary
}
}
impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
Self: ValidAmalgamation<'a, Key<P, R>>,
Self: PrimaryKey<'a, P, R>,
{
pub fn alive(&self) -> Result<()>
{
if ! self.primary() {
self.cert().alive()?;
}
let sig = {
let binding : &Signature = self.binding_signature();
if binding.key_validity_period().is_some() {
Some(binding)
} else {
self.direct_key_signature().ok()
}
};
if let Some(sig) = sig {
sig.key_alive(self.key(), self.time())
} else {
Ok(())
}
}
pub fn into_key_amalgamation(self) -> KeyAmalgamation<'a, P, R, R2> {
self.ka
}
}
impl<'a, P> ValidPrimaryKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
#[cfg(test)]
pub(crate) fn set_validity_period_as_of(&self,
primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>,
now: time::SystemTime)
-> Result<Vec<Signature>>
{
ValidErasedKeyAmalgamation::<P>::from(self)
.set_validity_period_as_of(primary_signer, None, expiration, now)
}
pub fn set_expiration_time(&self,
primary_signer: &mut dyn Signer,
expiration: Option<time::SystemTime>)
-> Result<Vec<Signature>>
{
ValidErasedKeyAmalgamation::<P>::from(self)
.set_expiration_time(primary_signer, None, expiration)
}
}
impl<'a, P> ValidSubordinateKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
pub fn set_expiration_time(&self,
primary_signer: &mut dyn Signer,
subkey_signer: Option<&mut dyn Signer>,
expiration: Option<time::SystemTime>)
-> Result<Vec<Signature>>
{
ValidErasedKeyAmalgamation::<P>::from(self)
.set_expiration_time(primary_signer, subkey_signer, expiration)
}
}
impl<'a, P> ValidErasedKeyAmalgamation<'a, P>
where P: 'a + key::KeyParts
{
pub(crate) fn set_validity_period_as_of(&self,
primary_signer: &mut dyn Signer,
subkey_signer:
Option<&mut dyn Signer>,
expiration: Option<time::Duration>,
now: time::SystemTime)
-> Result<Vec<Signature>>
{
let mut sigs = Vec::new();
if self.primary() {
let template = self.direct_key_signature()
.map(|sig| {
signature::SignatureBuilder::from(sig.clone())
})
.unwrap_or_else(|_| {
let mut template = signature::SignatureBuilder::from(
self.binding_signature().clone())
.set_type(SignatureType::DirectKey);
use SubpacketTag::*;
let ha = template.hashed_area_mut();
ha.remove_all(ExportableCertification);
ha.remove_all(Revocable);
ha.remove_all(TrustSignature);
ha.remove_all(RegularExpression);
ha.remove_all(PrimaryUserID);
ha.remove_all(SignersUserID);
ha.remove_all(ReasonForRevocation);
ha.remove_all(SignatureTarget);
ha.remove_all(EmbeddedSignature);
template
});
let mut builder = template
.set_signature_creation_time(now)?
.set_key_validity_period(expiration)?;
builder.hashed_area_mut().remove_all(
signature::subpacket::SubpacketTag::PrimaryUserID);
sigs.push(builder.sign_direct_key(primary_signer, None)?);
for userid in self.cert().userids().revoked(false) {
let binding_signature = userid.binding_signature();
let builder = signature::SignatureBuilder::from(binding_signature.clone())
.set_signature_creation_time(now)?
.set_key_validity_period(expiration)?
.set_primary_userid(
self.cert().primary_userid().map(|primary| {
userid.userid() == primary.userid()
}).unwrap_or(false))?;
sigs.push(builder.sign_userid_binding(primary_signer,
self.cert().primary_key().component(),
&userid)?);
}
} else {
let backsig = if self.for_certification() || self.for_signing() {
if let Some(subkey_signer) = subkey_signer {
Some(signature::SignatureBuilder::new(
SignatureType::PrimaryKeyBinding)
.set_signature_creation_time(now)?
.set_hash_algo(self.binding_signature.hash_algo())
.sign_primary_key_binding(
subkey_signer,
&self.cert().primary_key(),
self.key().role_as_subordinate())?)
} else {
return Err(Error::InvalidArgument(
"Changing expiration of signing-capable subkeys \
requires subkey signer".into()).into());
}
} else {
if subkey_signer.is_some() {
return Err(Error::InvalidArgument(
"Subkey signer given but subkey is not signing-capable"
.into()).into());
}
None
};
let mut sig =
signature::SignatureBuilder::from(
self.binding_signature().clone())
.set_signature_creation_time(now)?
.set_key_validity_period(expiration)?;
if let Some(bs) = backsig {
sig = sig.set_embedded_signature(bs)?;
}
sigs.push(sig.sign_subkey_binding(
primary_signer,
self.cert().primary_key().component(),
self.key().role_as_subordinate())?);
}
Ok(sigs)
}
pub fn set_expiration_time(&self,
primary_signer: &mut dyn Signer,
subkey_signer: Option<&mut dyn Signer>,
expiration: Option<time::SystemTime>)
-> Result<Vec<Signature>>
{
let expiration =
if let Some(e) = expiration.map(crate::types::normalize_systemtime)
{
let ct = self.creation_time();
match e.duration_since(ct) {
Ok(v) => Some(v),
Err(_) => return Err(Error::InvalidArgument(
format!("Expiration time {:?} predates creation time \
{:?}", e, ct)).into()),
}
} else {
None
};
self.set_validity_period_as_of(primary_signer, subkey_signer,
expiration, time::SystemTime::now())
}
}
impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
Self: ValidAmalgamation<'a, Key<P, R>>
{
pub fn key_flags(&self) -> Option<KeyFlags> {
self.map(|s| s.key_flags())
}
pub fn has_any_key_flag<F>(&self, flags: F) -> bool
where F: Borrow<KeyFlags>
{
let our_flags = self.key_flags().unwrap_or_else(KeyFlags::empty);
!(&our_flags & flags.borrow()).is_empty()
}
pub fn for_certification(&self) -> bool {
self.has_any_key_flag(KeyFlags::empty().set_certification())
}
pub fn for_signing(&self) -> bool {
self.has_any_key_flag(KeyFlags::empty().set_signing())
}
pub fn for_authentication(&self) -> bool
{
self.has_any_key_flag(KeyFlags::empty().set_authentication())
}
pub fn for_storage_encryption(&self) -> bool
{
self.has_any_key_flag(KeyFlags::empty().set_storage_encryption())
}
pub fn for_transport_encryption(&self) -> bool
{
self.has_any_key_flag(KeyFlags::empty().set_transport_encryption())
}
pub fn key_validity_period(&self) -> Option<std::time::Duration> {
self.map(|s| s.key_validity_period())
}
pub fn key_expiration_time(&self) -> Option<time::SystemTime> {
match self.key_validity_period() {
Some(vp) if vp.as_secs() > 0 => Some(self.key().creation_time() + vp),
_ => None,
}
}
}
#[cfg(test)]
mod test {
use crate::policy::StandardPolicy as P;
use crate::cert::prelude::*;
use crate::packet::Packet;
use super::*;
#[test]
fn expire_subkeys() {
let p = &P::new();
let now = time::SystemTime::now();
let a_year = time::Duration::from_secs(365 * 24 * 60 * 60);
let in_a_year = now + a_year;
let in_two_years = now + 2 * a_year;
let (cert, _) = CertBuilder::new()
.set_creation_time(now - a_year)
.add_signing_subkey()
.add_transport_encryption_subkey()
.generate().unwrap();
for ka in cert.keys().with_policy(p, None) {
assert!(ka.alive().is_ok());
}
let mut primary_signer = cert.primary_key().key().clone()
.parts_into_secret().unwrap().into_keypair().unwrap();
let mut signing_subkey_signer = cert.with_policy(p, None).unwrap()
.keys().for_signing().nth(0).unwrap()
.key().clone().parts_into_secret().unwrap()
.into_keypair().unwrap();
let sigs = cert.keys().subkeys().with_policy(p, None)
.flat_map(|ka| {
if ! ka.for_signing() {
ka.set_expiration_time(&mut primary_signer,
None,
Some(in_a_year)).unwrap()
} else {
ka.set_expiration_time(&mut primary_signer,
Some(&mut signing_subkey_signer),
Some(in_a_year)).unwrap()
}
.into_iter()
.map(Into::into)
})
.collect::<Vec<Packet>>();
let cert = cert.insert_packets(sigs).unwrap();
for ka in cert.keys().with_policy(p, None) {
assert!(ka.alive().is_ok());
}
assert!(cert.primary_key().with_policy(p, in_two_years).unwrap()
.alive().is_ok());
for ka in cert.keys().subkeys().with_policy(p, in_two_years) {
assert!(ka.alive().is_err());
}
}
#[test]
fn issue_564() -> Result<()> {
use crate::parse::Parse;
use crate::packet::signature::subpacket::SubpacketTag;
let p = &P::new();
let cert = Cert::from_bytes(crate::tests::key("testy.pgp"))?;
assert!(cert.with_policy(p, None)?.alive().is_err());
let subkey = cert.with_policy(p, None)?.keys().nth(1).unwrap();
assert!(subkey.binding_signature().hashed_area()
.subpacket(SubpacketTag::KeyExpirationTime).is_none());
assert!(subkey.alive().is_err());
Ok(())
}
#[test]
fn set_expiry_on_certificate_without_direct_signature() -> Result<()> {
use crate::policy::StandardPolicy;
let p = &StandardPolicy::new();
let (cert, _) =
CertBuilder::general_purpose(None, Some("alice@example.org"))
.set_validity_period(None)
.generate()?;
let cert = Cert::from_packets(Vec::from(cert)
.into_iter()
.filter(|p| {
match p {
Packet::Signature(s)
if s.typ() == SignatureType::DirectKey => false,
_ => true,
}
}))?;
let vc = cert.with_policy(p, None)?;
for ka in vc.keys() {
assert!(ka.alive().is_ok());
}
let t = time::SystemTime::now()
+ time::Duration::from_secs(7 * 24 * 60 * 60);
let mut signer = vc
.primary_key().key().clone().parts_into_secret()?
.into_keypair()?;
let sigs = vc.primary_key()
.set_expiration_time(&mut signer, Some(t))?;
assert!(sigs.iter().any(|s| {
s.typ() == SignatureType::DirectKey
}));
let cert = cert.insert_packets(sigs)?;
for ka in cert.keys() {
let ka = ka.with_policy(p, None)?;
assert!(ka.alive().is_ok());
let ka = ka.with_policy(p, t + std::time::Duration::new(1, 0))?;
assert!(ka.alive().is_err());
}
Ok(())
}
}