use std::time;
use std::time::SystemTime;
use std::ops::Deref;
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::{hash::Hash, Signer},
Error,
Packet,
packet::Key,
packet::key,
packet::key::KeyParts,
packet::signature,
packet::Signature,
policy::Policy,
Result,
SignatureType,
types::HashAlgorithm,
types::RevocationStatus,
};
pub trait PrimaryKey<'a, P, R>
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,
}
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> 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> 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> 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: key::PublicParts::convert_key_amalgamation(
self.ca.parts_into_unspecified()).expect("to 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.mark_role_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.mark_role_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.mark_role_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.mark_role_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,
}
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<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,
}
}
}
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_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> 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 direct_key_signature(&self) -> Result<&'a Signature> {
self.cert.cert.primary.binding_signature(self.policy(), self.time())
}
fn revoked(&self) -> RevocationStatus<'a> {
if self.primary() {
self.cert.revoked()
} else {
self.bundle()._revoked(self.policy(), self.time(),
true, Some(self.binding_signature))
}
}
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,
}
}
}
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>>
{
pub fn alive(&self) -> Result<()>
{
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, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
where P: 'a + key::KeyParts,
R: 'a + key::KeyRole,
R2: Copy,
Self: PrimaryKey<'a, P, R>,
{
pub(crate) fn set_validity_period_as_of(&self,
primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>,
now: time::SystemTime)
-> Result<Vec<Packet>>
{
let mut sigs = Vec::new();
let binding = self.binding_signature();
for template in [
if binding.typ() != SignatureType::DirectKey {
Some(binding)
} else {
None
},
if self.primary() {
self.direct_key_signature().ok()
} else {
None
},
].iter().filter_map(|&x| x) {
let hash_algo = HashAlgorithm::SHA512;
let mut hash = hash_algo.context()?;
self.cert().primary.key().hash(&mut hash);
match template.typ() {
SignatureType::DirectKey =>
(),
SignatureType::GenericCertification
| SignatureType::PersonaCertification
| SignatureType::CasualCertification
| SignatureType::PositiveCertification =>
self.cert.primary_userid()
.expect("this type must be from a userid binding, \
hence there must be a userid valid at `now`")
.userid().hash(&mut hash),
SignatureType::SubkeyBinding =>
self.key().hash(&mut hash),
_ => unreachable!(),
}
sigs.push(signature::Builder::from(template.clone())
.set_key_validity_period(expiration)?
.set_signature_creation_time(now)?
.sign_hash(primary_signer, hash)?.into());
}
Ok(sigs)
}
pub fn set_expiration_time(&self,
primary_signer: &mut dyn Signer,
expiration: Option<time::SystemTime>)
-> Result<Vec<Packet>>
{
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, expiration,
time::SystemTime::now())
}
}
#[cfg(test)]
mod test {
use crate::policy::StandardPolicy as P;
use crate::cert::prelude::*;
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 = cert.primary_key().key().clone()
.parts_into_secret().unwrap().into_keypair().unwrap();
let sigs = cert.keys().subkeys().with_policy(p, None)
.flat_map(|ka| {
ka.set_expiration_time(&mut primary, Some(in_a_year))
.unwrap()
.into_iter()
.map(Into::into)
})
.collect::<Vec<Packet>>();
let cert = cert.merge_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());
}
}
}