use std::time;
use std::time::SystemTime;
use std::borrow::Borrow;
use std::convert::TryFrom;
use std::convert::TryInto;
use anyhow::Context;
use crate::{
Cert,
cert::bundle::KeyBundle,
cert::amalgamation::{
ComponentAmalgamation,
key::signature::subpacket::SubpacketValue,
ValidAmalgamation,
ValidBindingSignature,
ValidateAmalgamation,
},
cert::ValidCert,
crypto::Signer,
Error,
packet::Key,
packet::key,
packet::Signature,
packet::signature,
packet::signature::subpacket::SubpacketTag,
policy::Policy,
Result,
seal,
types::{
KeyFlags,
RevocationKey,
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, PartialEq)]
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> ComponentAmalgamation<'a, Key<P, R>>
where
P: key::KeyParts,
R: key::KeyRole,
{
pub fn key(&self) -> &Key<P, R> {
self.component()
}
pub(crate) fn set_role(&mut self, _: key::KeyRoleRT) {
}
pub(crate) fn has_secret(&self) -> bool {
self.key().has_secret()
}
}
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, ()>;
impl<'a, P> SubordinateKeyAmalgamation<'a, P>
where
P: key::KeyParts,
{
pub fn revocation_status<T>(&self, policy: &dyn Policy, t: T)
-> RevocationStatus
where
T: Into<Option<time::SystemTime>>,
{
let t = t.into();
self.bundle().revocation_status(policy, t)
}
}
pub type ErasedKeyAmalgamation<'a, P>
= KeyAmalgamation<'a, P, key::UnspecifiedRole, bool>;
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.clone().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.clone().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(crate::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.clone().parts_into_public(),
primary: self.primary,
},
cert: ValidCert {
cert,
policy,
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> PrimaryKeyAmalgamation<'a, P>
where
P: key::KeyParts,
{
pub fn binding_signature<T>(&self, policy: &dyn Policy, time: T)
-> Result<&'a Signature>
where T: Into<Option<time::SystemTime>>
{
let time = time.into().unwrap_or_else(crate::now);
self.bundle().binding_signature(policy, time)
}
}
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: (),
}
}
pub fn binding_signature<T>(&self, policy: &dyn Policy, time: T)
-> Result<&'a Signature>
where T: Into<Option<time::SystemTime>>
{
let time = time.into().unwrap_or_else(crate::now);
self.bundle().binding_signature(policy, time)
}
}
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(crate::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)
.map_err(|e1| {
if let Some(Error::NoBindingSignature(_))
= e1.downcast_ref()
{
e0 } else {
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 cert(&self) -> &'a Cert {
self.ca.cert()
}
pub fn bundle(&self) -> &'a crate::cert::ComponentBundle<Key<P, R>> {
self.ca.bundle()
}
pub fn component(&self) -> &'a Key<P, R> {
self.bundle().component()
}
pub fn key(&self) -> &'a Key<P, R> {
self.component()
}
pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ca.self_signatures()
}
pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ca.certifications()
}
pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ca.self_revocations()
}
pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ca.other_revocations()
}
pub fn signatures(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync {
self.ca.signatures()
}
pub(crate) fn has_secret(&self) -> bool {
self.key().has_secret()
}
}
impl<'a, P, R, R2> KeyAmalgamation<'a, P, R, R2>
where Self: PrimaryKey<'a, P, R>,
P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
{
pub fn valid_certifications_by_key<T, PK>(&self,
policy: &'a dyn Policy,
reference_time: T,
issuer: PK)
-> impl Iterator<Item=&Signature> + Send + Sync
where
T: Into<Option<time::SystemTime>>,
PK: Into<&'a Key<key::PublicParts,
key::UnspecifiedRole>>,
{
let reference_time = reference_time.into();
let issuer = issuer.into();
let primary = self.primary();
self.ca.valid_certifications_by_key_(
policy, reference_time, issuer, false,
self.certifications(),
move |sig| {
if primary {
sig.clone().verify_direct_key(
issuer,
self.component().role_as_primary())
} else {
sig.clone().verify_subkey_binding(
issuer,
self.cert().primary_key().key(),
self.component().role_as_subordinate())
}
})
}
pub fn active_certifications_by_key<T, PK>(&self,
policy: &'a dyn Policy,
reference_time: T,
issuer: PK)
-> impl Iterator<Item=&Signature> + Send + Sync
where
T: Into<Option<time::SystemTime>>,
PK: Into<&'a Key<key::PublicParts,
key::UnspecifiedRole>>,
{
let reference_time = reference_time.into();
let issuer = issuer.into();
let primary = self.primary();
self.ca.valid_certifications_by_key_(
policy, reference_time, issuer, true,
self.certifications(),
move |sig| {
if primary {
sig.clone().verify_direct_key(
issuer,
self.component().role_as_primary())
} else {
sig.clone().verify_subkey_binding(
issuer,
self.cert().primary_key().key(),
&self.component().role_as_subordinate())
}
})
}
pub fn valid_third_party_revocations_by_key<T, PK>(&self,
policy: &'a dyn Policy,
reference_time: T,
issuer: PK)
-> impl Iterator<Item=&Signature> + Send + Sync
where
T: Into<Option<time::SystemTime>>,
PK: Into<&'a Key<key::PublicParts,
key::UnspecifiedRole>>,
{
let issuer = issuer.into();
let reference_time = reference_time.into();
let primary = self.primary();
self.ca.valid_certifications_by_key_(
policy, reference_time, issuer, false,
self.other_revocations(),
move |sig| {
if primary {
sig.clone().verify_primary_key_revocation(
issuer,
self.component().role_as_primary())
} else {
sig.clone().verify_subkey_revocation(
issuer,
self.cert().primary_key().key(),
&self.component().role_as_subordinate())
}
})
}
}
#[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,
);
impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
where
P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
{
pub fn cert(&self) -> &'a Cert {
self.ka.cert()
}
pub fn binding_signature(&self) -> &'a Signature {
self.binding_signature
}
pub fn amalgamation(&self) -> &KeyAmalgamation<'a, P, R, R2> {
&self.ka
}
pub fn bundle(&self) -> &'a crate::cert::ComponentBundle<Key<P, R>> {
self.ka.bundle()
}
pub fn component(&self) -> &'a Key<P, R> {
self.bundle().component()
}
pub fn key(&self) -> &'a Key<P, R> {
self.component()
}
pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ka.self_signatures()
}
pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ka.certifications()
}
pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ka.self_revocations()
}
pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.ka.other_revocations()
}
pub fn signatures(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync {
self.ka.signatures()
}
pub(crate) fn has_secret(&self) -> bool {
self.key().has_secret()
}
}
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> 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 valid_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_intern(
self.policy(), Some(self.time()), true,
Some(self.binding_signature))
}
}
fn revocation_keys(&self)
-> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>
{
let mut keys = std::collections::HashSet::new();
let policy = self.policy();
let pk_sec = self.cert().primary_key().key().hash_algo_security();
let sec = self.bundle().hash_algo_security;
self.self_signatures()
.filter(move |sig| {
policy.signature(sig, sec).is_ok()
})
.chain(self.cert().primary_key()
.self_signatures()
.filter(|sig| {
policy.signature(sig, pk_sec).is_ok()
}))
.flat_map(|sig| sig.revocation_keys())
.for_each(|rk| { keys.insert(rk); });
Box::new(keys.into_iter())
}
}
impl<'a, P, R, R2> ValidBindingSignature<'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>,
{}
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.valid_cert().alive()
.context("The certificate is not live")?;
}
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())
.with_context(|| if self.primary() {
"The primary key is not live"
} else {
"The subkey is not live"
})
} else {
Ok(())
}
}
}
impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
where P: key::KeyParts,
R: key::KeyRole,
R2: Copy,
Self: PrimaryKey<'a, P, R>,
{
pub fn primary_key_binding_signature(&self) -> Option<&Signature> {
let subkey = if self.primary() {
return None;
} else {
self.key().role_as_subordinate()
};
let pk = self.cert().primary_key().key();
for backsig in
self.binding_signature.subpackets(SubpacketTag::EmbeddedSignature)
{
if let SubpacketValue::EmbeddedSignature(sig) =
backsig.value()
{
if sig.verify_primary_key_binding(pk, subkey).is_ok() {
backsig.set_authenticated(true);
return Some(sig);
}
} else {
unreachable!("subpackets(EmbeddedSignature) returns \
EmbeddedSignatures");
}
}
None
}
}
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.valid_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.valid_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.userid())?);
}
} else {
let backsig = if self.for_certification() || self.for_signing()
|| self.for_authentication()
{
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().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.key().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, crate::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>>,
Self: ValidBindingSignature<'a, Key<P, R>>,
{
pub fn key_flags(&self) -> Option<KeyFlags> {
self.map(|s| s.key_flags())
.or_else(|| {
use crate::types::PublicKeyAlgorithm;
let is_primary = false;
#[allow(deprecated)]
match (is_primary, self.key().pk_algo()) {
(true, PublicKeyAlgorithm::RSAEncryptSign) =>
Some(KeyFlags::empty()
.set_certification()
.set_transport_encryption()
.set_storage_encryption()
.set_signing()),
(true, _) =>
Some(KeyFlags::empty()
.set_certification()
.set_signing()),
(false, PublicKeyAlgorithm::RSAEncryptSign) =>
Some(KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption()
.set_signing()),
(false,
| PublicKeyAlgorithm::RSASign
| PublicKeyAlgorithm::DSA) =>
Some(KeyFlags::empty().set_signing()),
(false,
| PublicKeyAlgorithm::RSAEncrypt
| PublicKeyAlgorithm::ElGamalEncrypt
| PublicKeyAlgorithm::ElGamalEncryptSign) =>
Some(KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption()),
(false, _) => None,
}
})
}
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 std::time::Duration;
use std::time::UNIX_EPOCH;
use crate::policy::StandardPolicy as P;
use crate::cert::prelude::*;
use crate::packet::Packet;
use crate::packet::signature::SignatureBuilder;
use crate::types::ReasonForRevocation;
use crate::types::RevocationType;
use super::*;
#[test]
fn expire_subkeys() {
let p = &P::new();
let now = crate::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().next().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().0;
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(Some("alice@example.org"))
.set_validity_period(None)
.generate()?;
let cert = Cert::from_packets(
cert.as_tsk().into_packets()
.filter(|p| ! matches!(
p,
Packet::Signature(s) if s.typ() == SignatureType::DirectKey
)))?;
let vc = cert.with_policy(p, None)?;
for ka in vc.keys() {
assert!(ka.alive().is_ok());
}
let t = crate::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)?.0;
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(())
}
#[test]
fn key_amalgamation_certifications_by_key() -> Result<()> {
let p = &crate::policy::StandardPolicy::new();
let t0 = UNIX_EPOCH + Duration::new(1704200400, 0);
let t1 = UNIX_EPOCH + Duration::new(1704204000, 0);
let t2 = UNIX_EPOCH + Duration::new(1704207600, 0);
let (alice, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid("<alice@example.example>")
.generate()
.unwrap();
let alice_primary = alice.primary_key().key();
let (bob, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid("<bob@example.example>")
.generate()
.unwrap();
let bob_primary = bob.primary_key().key();
let carol_userid = "<carol@example.example>";
let (carol, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid(carol_userid)
.generate()
.unwrap();
let ka = alice.primary_key();
assert_eq!(
ka.valid_certifications_by_key(p, None, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, None, alice_primary).count(),
0);
let ka = bob.primary_key();
assert_eq!(
ka.valid_certifications_by_key(p, None, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, None, alice_primary).count(),
0);
let ka = carol.primary_key();
assert_eq!(
ka.valid_certifications_by_key(p, None, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, None, alice_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(t1)?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(certification.clone())?.0;
let ka = carol.primary_key();
assert_eq!(ka.certifications().count(), 1);
assert_eq!(
ka.valid_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, alice_primary).count(),
1);
assert_eq!(
ka.active_certifications_by_key(p, t1, alice_primary).count(),
1);
assert_eq!(
ka.valid_certifications_by_key(p, t1, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t1, bob_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(t1)?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(certification.clone())?.0;
let ka = carol.primary_key();
assert_eq!(ka.certifications().count(), 2);
assert_eq!(
ka.valid_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t2, alice_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t2, alice_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, bob_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(t2)?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(certification.clone())?.0;
let ka = carol.primary_key();
assert_eq!(ka.certifications().count(), 3);
assert_eq!(
ka.valid_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.active_certifications_by_key(p, t2, alice_primary).count(),
1);
assert_eq!(
ka.valid_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, bob_primary).count(),
0);
let mut bob_signer = bob.primary_key()
.key()
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(t1)?
.set_signature_validity_period(t2.duration_since(t1)?)?
.sign_direct_key(
&mut bob_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(certification.clone())?.0;
let ka = carol.primary_key();
assert_eq!(ka.certifications().count(), 4);
assert_eq!(
ka.valid_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.active_certifications_by_key(p, t2, alice_primary).count(),
1);
assert_eq!(
ka.valid_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, bob_primary).count(),
1);
assert_eq!(
ka.active_certifications_by_key(p, t1, bob_primary).count(),
1);
assert_eq!(
ka.valid_certifications_by_key(p, t2, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t2, bob_primary).count(),
0);
let mut bob_signer = bob.primary_key()
.key()
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(t1)?
.sign_direct_key(
&mut bob_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(certification.clone())?.0;
let ka = carol.primary_key();
assert_eq!(ka.certifications().count(), 5);
assert_eq!(
ka.valid_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, alice_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.active_certifications_by_key(p, t2, alice_primary).count(),
1);
assert_eq!(
ka.valid_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.active_certifications_by_key(p, t0, bob_primary).count(),
0);
assert_eq!(
ka.valid_certifications_by_key(p, t1, bob_primary).count(),
2);
assert_eq!(
ka.active_certifications_by_key(p, t1, bob_primary).count(),
2);
assert_eq!(
ka.valid_certifications_by_key(p, t2, bob_primary).count(),
1);
assert_eq!(
ka.active_certifications_by_key(p, t2, bob_primary).count(),
1);
Ok(())
}
fn key_amalgamation_valid_third_party_revocations_by_key(
reason: ReasonForRevocation)
-> Result<()>
{
let soft = reason.revocation_type() == RevocationType::Soft;
let p = &crate::policy::StandardPolicy::new();
let t0 = UNIX_EPOCH + Duration::new(1704200400, 0);
let t1 = UNIX_EPOCH + Duration::new(1704204000, 0);
let t2 = UNIX_EPOCH + Duration::new(1704207600, 0);
let (alice, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid("<alice@example.example>")
.generate()
.unwrap();
let alice_primary = alice.primary_key().key();
let (bob, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid("<bob@example.example>")
.generate()
.unwrap();
let bob_primary = bob.primary_key().key();
let carol_userid = "<carol@example.example>";
let (carol, _) = CertBuilder::new()
.set_creation_time(t0)
.add_userid(carol_userid)
.generate()
.unwrap();
let ka = alice.primary_key();
assert_eq!(
ka.valid_third_party_revocations_by_key(p, None, alice_primary).count(),
0);
let ka = bob.primary_key();
assert_eq!(
ka.valid_third_party_revocations_by_key(p, None, alice_primary).count(),
0);
let ka = carol.primary_key();
assert_eq!(
ka.valid_third_party_revocations_by_key(p, None, alice_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let rev = SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(
reason, b"")?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(rev)?.0;
let ka = carol.primary_key();
assert_eq!(ka.other_revocations().count(), 1);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, alice_primary).count(),
if soft { 0 } else { 1 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, alice_primary).count(),
1);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, bob_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let rev = SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(reason, b"")?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(rev)?.0;
let ka = carol.primary_key();
assert_eq!(ka.other_revocations().count(), 2);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, alice_primary).count(),
if soft { 0 } else { 2 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, alice_primary).count(),
2);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, alice_primary).count(),
2);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, bob_primary).count(),
0);
let mut alice_signer = alice_primary
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let rev = SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t2)?
.set_reason_for_revocation(reason, b"")?
.sign_direct_key(
&mut alice_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(rev)?.0;
let ka = carol.primary_key();
assert_eq!(ka.other_revocations().count(), 3);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, alice_primary).count(),
if soft { 0 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, alice_primary).count(),
if soft { 2 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, bob_primary).count(),
0);
let mut bob_signer = bob.primary_key()
.key()
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let rev = SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t1)?
.set_signature_validity_period(t2.duration_since(t1)?)?
.set_reason_for_revocation(reason, b"")?
.sign_direct_key(
&mut bob_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(rev)?.0;
let ka = carol.primary_key();
assert_eq!(ka.other_revocations().count(), 4);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, alice_primary).count(),
if soft { 0 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, alice_primary).count(),
if soft { 2 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, bob_primary).count(),
if soft { 0 } else { 1 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, bob_primary).count(),
1);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, bob_primary).count(),
if soft { 0 } else { 1 });
let mut bob_signer = bob.primary_key()
.key()
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let rev = SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(reason, b"")?
.sign_direct_key(
&mut bob_signer,
carol.primary_key().key())?;
let carol = carol.insert_packets(rev)?.0;
let ka = carol.primary_key();
assert_eq!(
ka.other_revocations().count(), 5);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, alice_primary).count(),
if soft { 0 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, alice_primary).count(),
if soft { 2 } else { 3 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, alice_primary).count(),
3);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t0, bob_primary).count(),
if soft { 0 } else { 2 });
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t1, bob_primary).count(),
2);
assert_eq!(
ka.valid_third_party_revocations_by_key(p, t2, bob_primary).count(),
if soft { 1 } else { 2 });
Ok(())
}
#[test]
fn key_amalgamation_valid_third_party_revocations_by_key_soft()
-> Result<()>
{
key_amalgamation_valid_third_party_revocations_by_key(
ReasonForRevocation::KeyRetired)
}
#[test]
fn key_amalgamation_valid_third_party_revocations_by_key_hard()
-> Result<()>
{
key_amalgamation_valid_third_party_revocations_by_key(
ReasonForRevocation::KeyCompromised)
}
}