use std::time;
use std::time::{
Duration,
SystemTime,
};
use std::clone::Clone;
use std::borrow::Borrow;
use crate::{
cert::prelude::*,
crypto::{Signer, hash::Hash},
Error,
KeyHandle,
packet,
packet::{
Key,
Signature,
Unknown,
UserAttribute,
UserID,
key::{PrimaryRole, PublicParts},
},
Result,
policy::{
HashAlgoSecurity,
Policy,
},
seal,
types::{
AEADAlgorithm,
CompressionAlgorithm,
Features,
HashAlgorithm,
KeyServerPreferences,
RevocationKey,
RevocationStatus,
RevocationType,
SignatureType,
SymmetricAlgorithm,
},
};
mod iter;
pub use iter::{
ComponentAmalgamationIter,
UnknownComponentAmalgamationIter,
UserAttributeAmalgamationIter,
UserIDAmalgamationIter,
ValidComponentAmalgamationIter,
ValidUserAttributeAmalgamationIter,
ValidUserIDAmalgamationIter,
};
pub mod key;
pub trait ValidateAmalgamation<'a, C: 'a>: seal::Sealed {
type V;
fn with_policy<T>(&self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized;
}
trait ValidateAmalgamationRelaxed<'a, C: 'a> {
type V;
fn with_policy_relaxed<T>(&self, policy: &'a dyn Policy, time: T,
valid_cert: bool) -> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized;
}
pub trait ValidAmalgamation<'a, C: 'a>: seal::Sealed
{
fn valid_cert(&self) -> &ValidCert<'a>;
fn time(&self) -> SystemTime;
fn policy(&self) -> &'a dyn Policy;
fn binding_signature(&self) -> &'a Signature;
fn direct_key_signature(&self) -> Result<&'a Signature> {
self.valid_cert().cert().primary_key()
.binding_signature(self.policy(), self.time())
}
fn revocation_status(&self) -> RevocationStatus<'a>;
fn revocation_keys(&self)
-> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>;
}
#[test]
fn valid_amalgamation_is_dyn_compatible() {
let _t: Option<Box<dyn ValidAmalgamation<()>>> = None;
}
pub trait ValidBindingSignature<'a, C: 'a>: ValidAmalgamation<'a, C> + seal::Sealed
{
fn map<F: Fn(&'a Signature) -> Option<T>, T>(&self, f: F) -> Option<T> {
f(self.binding_signature())
.or_else(|| self.direct_key_signature().ok().and_then(f))
}
}
#[derive(Debug, PartialEq)]
pub struct ComponentAmalgamation<'a, C> {
cert: &'a Cert,
bundle: &'a ComponentBundle<C>,
}
assert_send_and_sync!(ComponentAmalgamation<'_, C> where C);
pub type UserIDAmalgamation<'a> = ComponentAmalgamation<'a, UserID>;
pub type UserAttributeAmalgamation<'a>
= ComponentAmalgamation<'a, UserAttribute>;
pub type UnknownComponentAmalgamation<'a>
= ComponentAmalgamation<'a, Unknown>;
impl<'a, C> Clone for ComponentAmalgamation<'a, C> {
fn clone(&self) -> Self {
Self {
cert: self.cert,
bundle: self.bundle,
}
}
}
impl<'a, C> ComponentAmalgamation<'a, C> {
pub(crate) fn new(cert: &'a Cert, bundle: &'a ComponentBundle<C>) -> Self
{
Self {
cert,
bundle,
}
}
pub fn cert(&self) -> &'a Cert {
self.cert
}
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)
}
pub fn bundle(&self) -> &'a ComponentBundle<C> {
self.bundle
}
pub fn component(&self) -> &'a C {
self.bundle().component()
}
pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().self_signatures()
}
pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().certifications()
}
pub fn certifications_by_key<'b>(&'b self, issuers: &'b [ KeyHandle ])
-> impl Iterator<Item=&'a Signature> + Send + Sync + 'b
{
self.certifications().filter(|certification| {
certification.get_issuers().into_iter().any(|certification_issuer| {
issuers.iter().any(|issuer| {
certification_issuer.aliases(issuer)
})
})
})
}
pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().self_revocations()
}
pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().other_revocations()
}
pub fn approvals(&self)
-> impl Iterator<Item=&'a Signature> + Send + Sync + 'a
{
self.bundle().approvals()
}
pub fn signatures(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync {
self.bundle().signatures()
}
fn valid_certifications_by_key_<'b, F>(
&self,
policy: &'a dyn Policy,
reference_time: Option<time::SystemTime>,
issuer: &'a packet::Key<packet::key::PublicParts,
packet::key::UnspecifiedRole>,
only_active: bool,
certifications: impl Iterator<Item=&'b Signature> + Send + Sync,
verify_certification: F)
-> impl Iterator<Item=&'b Signature> + Send + Sync
where
F: Fn(&Signature) -> Result<()>
{
let reference_time = reference_time.unwrap_or_else(crate::now);
let issuer_handle = issuer.key_handle();
let issuer_handle = &issuer_handle;
let mut certifications: Vec<(&Signature, _)> = certifications
.filter_map(|certification| {
certification
.signature_creation_time()
.map(|ct| (certification, ct))
})
.filter(|(certification, _ct)| {
certification.get_issuers().into_iter().any(|sig_issuer| {
sig_issuer.aliases(issuer_handle)
})
})
.map(|(certification, ct)| {
let hard = if matches!(certification.typ(),
SignatureType::KeyRevocation
| SignatureType::SubkeyRevocation
| SignatureType::CertificationRevocation)
{
certification.reason_for_revocation()
.map(|(reason, _text)| {
reason.revocation_type() == RevocationType::Hard
})
.unwrap_or(true)
} else {
false
};
(certification, ct, hard)
})
.filter(|(_certification, ct, hard)| {
*ct <= reference_time || *hard
})
.filter(|(certification, ct, hard)| {
if *hard {
true
} else if let Some(validity)
= certification.signature_validity_period()
{
if validity == Duration::new(0, 0) {
true
} else {
*ct + validity > reference_time
}
} else {
true
}
})
.filter(|(_certification, ct, hard)| {
self.cert.primary_key().key().creation_time() <= *ct || *hard
})
.filter(|(certification, _ct, _hard)| {
policy
.signature(certification,
HashAlgoSecurity::CollisionResistance)
.is_ok()
})
.map(|(certification, ct, _hard)| (certification, ct))
.collect();
certifications.sort_unstable_by(|(_, a), (_, b)| {
a.cmp(b).reverse()
});
let mut valid = Vec::new();
for (certification, ct) in certifications.into_iter() {
if only_active {
if let Some((_active, active_ct)) = valid.get(0) {
if *active_ct != ct {
break;
}
}
}
if let Ok(()) = verify_certification(certification) {
valid.push((certification, ct));
}
}
valid.into_iter()
.map(|(certification, _creation_time)| certification)
.collect::<Vec<&Signature>>()
.into_iter()
}
}
macro_rules! impl_with_policy {
($func:ident, $value:ident $(, $arg:ident: $type:ty )*) => {
fn $func<T>(&self, policy: &'a dyn Policy, time: T, $($arg: $type, )*)
-> Result<Self::V>
where T: Into<Option<time::SystemTime>>,
Self: Sized
{
let time = time.into().unwrap_or_else(crate::now);
if $value {
self.cert.with_policy(policy, time)?;
}
let binding_signature = self.binding_signature(policy, time)?;
let cert = self.cert;
Ok(ValidComponentAmalgamation {
ca: self.clone(),
cert: ValidCert {
cert,
policy,
time,
},
binding_signature,
})
}
}
}
impl<'a, C> seal::Sealed for ComponentAmalgamation<'a, C> {}
impl<'a, C> ValidateAmalgamation<'a, C> for ComponentAmalgamation<'a, C> {
type V = ValidComponentAmalgamation<'a, C>;
impl_with_policy!(with_policy, true);
}
impl<'a, C> ValidateAmalgamationRelaxed<'a, C> for ComponentAmalgamation<'a, C> {
type V = ValidComponentAmalgamation<'a, C>;
impl_with_policy!(with_policy_relaxed, valid_cert, valid_cert: bool);
}
impl<'a> UserIDAmalgamation<'a> {
pub fn userid(&self) -> &'a UserID {
self.component()
}
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 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 packet::Key<packet::key::PublicParts,
packet::key::UnspecifiedRole>>,
{
let reference_time = reference_time.into();
let issuer = issuer.into();
self.valid_certifications_by_key_(
policy, reference_time, issuer, false,
self.certifications(),
|sig| {
sig.clone().verify_userid_binding(
issuer,
self.cert.primary_key().key(),
self.userid())
})
}
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 packet::Key<packet::key::PublicParts,
packet::key::UnspecifiedRole>>,
{
let reference_time = reference_time.into();
let issuer = issuer.into();
self.valid_certifications_by_key_(
policy, reference_time, issuer, true,
self.certifications(),
|sig| {
sig.clone().verify_userid_binding(
issuer,
self.cert.primary_key().key(),
self.userid())
})
}
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 packet::Key<packet::key::PublicParts,
packet::key::UnspecifiedRole>>,
{
let reference_time = reference_time.into();
let issuer = issuer.into();
self.valid_certifications_by_key_(
policy, reference_time, issuer, false,
self.other_revocations(),
|sig| {
sig.clone().verify_userid_revocation(
issuer,
self.cert.primary_key().key(),
self.userid())
})
}
pub fn approve_of_certifications<T, C, S>(&self,
policy: &dyn Policy,
time: T,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where T: Into<Option<time::SystemTime>>,
C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
let time = time.into();
let certifications = certifications.into_iter()
.collect::<Vec<_>>();
let old = self.clone()
.with_policy(policy, time)
.ok()
.and_then(
|v| v.certification_approval_key_signatures().next().cloned());
approve_of_certifications_common(self.cert().primary_key().key(),
self.userid(),
old, time, primary_signer,
&certifications)
}
}
impl<'a> UserAttributeAmalgamation<'a> {
pub fn user_attribute(&self) -> &'a UserAttribute {
self.component()
}
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 fn approve_of_certifications<T, C, S>(&self,
policy: &dyn Policy,
time: T,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where T: Into<Option<time::SystemTime>>,
C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
let time = time.into();
let certifications = certifications.into_iter()
.collect::<Vec<_>>();
let old = self.clone()
.with_policy(policy, time)
.ok()
.and_then(
|v| v.certification_approval_key_signatures().next().cloned());
approve_of_certifications_common(self.cert().primary_key().key(),
self.user_attribute(),
old, time, primary_signer,
&certifications)
}
}
fn approve_of_certifications_common<S>(key: &Key<PublicParts, PrimaryRole>,
component: &dyn Hash,
old_attestation: Option<Signature>,
time: Option<SystemTime>,
primary_signer: &mut dyn Signer,
certifications: &[S])
-> Result<Vec<Signature>>
where
S: Borrow<Signature>,
{
use crate::{
packet::signature::{SignatureBuilder, subpacket::SubpacketArea},
serialize::MarshalInto,
};
let now = time.unwrap_or_else(crate::now);
let hash_algo = HashAlgorithm::default();
let digest_size = hash_algo.digest_size()?;
let mut attestations = Vec::new();
for certification in certifications {
let mut h = hash_algo.context()?
.for_signature(primary_signer.public().version());
certification.borrow().hash_for_confirmation(&mut h)?;
attestations.push(h.into_digest()?);
}
attestations.sort();
let template = if let Some(old) = old_attestation {
let mut s = SignatureBuilder::from(old)
.set_reference_time(now)?;
s.hashed_area_mut().clear();
s.unhashed_area_mut().clear();
s
} else {
use crate::packet::signature::SIG_BACKDATE_BY;
let mut creation_time =
now - time::Duration::new(SIG_BACKDATE_BY, 0);
let key_creation_time = primary_signer.public().creation_time();
if creation_time < key_creation_time {
creation_time = key_creation_time.min(now);
}
let template = SignatureBuilder::new(SignatureType::CertificationApproval)
.set_signature_creation_time(creation_time)?;
template
};
let template = template
.set_hash_algo(hash_algo);
let available_space = {
let t = template.clone().pre_sign(primary_signer)?;
SubpacketArea::MAX_SIZE - t.hashed_area().serialized_len()
};
const SUBPACKET_HEADER_MAX_LEN: usize = 5 + 1;
let digests_per_sig =
(available_space - SUBPACKET_HEADER_MAX_LEN) / digest_size;
let mut sigs = Vec::new();
for digests in attestations.chunks(digests_per_sig) {
let t = template.clone().pre_sign(primary_signer)?;
let mut hash = hash_algo.context()?
.for_signature(primary_signer.public().version());
if let Some(salt) = t.sb_version.salt() {
hash.update(salt);
}
key.hash(&mut hash)?;
component.hash(&mut hash)?;
sigs.push(t
.set_approved_certifications(digests)?
.sign_hash(primary_signer, hash)?);
}
if attestations.is_empty() {
assert!(sigs.is_empty());
let t = template.clone().pre_sign(primary_signer)?;
let mut hash = hash_algo.context()?
.for_signature(primary_signer.public().version());
if let Some(salt) = t.sb_version.salt() {
hash.update(salt);
}
key.hash(&mut hash)?;
component.hash(&mut hash)?;
sigs.push(t
.set_approved_certifications(Option::<&[u8]>::None)?
.sign_hash(primary_signer, hash.clone())?);
}
Ok(sigs)
}
impl<'a> UnknownComponentAmalgamation<'a> {
pub fn unknown(&self) -> &'a Unknown {
self.component()
}
}
#[derive(Debug)]
pub struct ValidComponentAmalgamation<'a, C> {
ca: ComponentAmalgamation<'a, C>,
cert: ValidCert<'a>,
binding_signature: &'a Signature,
}
assert_send_and_sync!(ValidComponentAmalgamation<'_, C> where C);
impl<'a, C> ValidComponentAmalgamation<'a, C> {
pub fn cert(&self) -> &'a Cert {
self.ca.cert()
}
pub fn binding_signature(&self) -> &'a Signature {
self.binding_signature
}
pub fn amalgamation(&self) -> &ComponentAmalgamation<'a, C> {
&self.ca
}
pub fn bundle(&self) -> &'a ComponentBundle<C> {
self.ca.bundle()
}
pub fn component(&self) -> &'a C {
self.bundle().component()
}
}
impl<'a, C> ValidComponentAmalgamation<'a, C>
where
C: Send + Sync,
{
pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
let policy = self.cert.policy();
let has = self.ca.bundle().hash_algo_security;
self.ca.self_signatures()
.filter(move |sig| policy.signature(sig, has).is_ok())
}
pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
let policy = self.cert.policy();
self.ca.certifications()
.filter(move |sig| policy.signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
let policy = self.cert.policy();
let has = self.ca.bundle().hash_algo_security;
self.ca.self_revocations()
.filter(move |sig| policy.signature(sig, has).is_ok())
}
pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
let policy = self.cert.policy();
self.ca.other_revocations()
.filter(move |sig| policy.signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
pub fn approvals(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync + 'a
{
let policy = self.cert.policy();
let has = self.ca.bundle().hash_algo_security;
self.ca.approvals()
.filter(move |sig| policy.signature(sig, has).is_ok())
}
pub fn signatures(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync + 'a {
let policy = self.cert.policy();
self.ca.signatures()
.filter(move |sig| policy.signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
}
pub type ValidUserIDAmalgamation<'a> = ValidComponentAmalgamation<'a, UserID>;
impl<'a> ValidUserIDAmalgamation<'a> {
pub fn userid(&self) -> &'a UserID {
self.component()
}
pub fn approved_certifications(&self)
-> impl Iterator<Item=&Signature> + Send + Sync
{
let mut hash_algo = None;
let digests: std::collections::HashSet<_> =
self.certification_approval_key_signatures()
.filter_map(|sig| {
sig.approved_certifications().ok()
.map(|digest_iter| (sig, digest_iter))
})
.flat_map(|(sig, digest_iter)| {
hash_algo = Some(sig.hash_algo());
digest_iter
})
.collect();
self.certifications()
.filter_map(move |sig| {
let mut hash = hash_algo.and_then(|a| a.context().ok())?
.for_signature(sig.version());
sig.hash_for_confirmation(&mut hash).ok()?;
let digest = hash.into_digest().ok()?;
if digests.contains(&digest[..]) {
Some(sig)
} else {
None
}
})
}
pub fn certification_approval_key_signatures(&'a self)
-> impl Iterator<Item=&'a Signature> + Send + Sync
{
let mut first = None;
self.ca.approvals()
.filter(move |sig| self.cert.policy().signature(
sig,
HashAlgoSecurity::CollisionResistance).is_ok())
.take_while(move |sig| {
let time_hash = (
if let Some(t) = sig.signature_creation_time() {
(t, sig.hash_algo())
} else {
return false;
},
sig.hash_algo());
if let Some(reference) = first {
reference == time_hash
} else {
first = Some(time_hash);
true
}
})
}
pub fn approve_of_certifications<C, S>(&self,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
self.ca
.approve_of_certifications(self.policy(),
self.time(),
primary_signer,
certifications)
}
}
pub type ValidUserAttributeAmalgamation<'a>
= ValidComponentAmalgamation<'a, UserAttribute>;
impl<'a> ValidUserAttributeAmalgamation<'a> {
pub fn user_attribute(&self) -> &'a UserAttribute {
self.component()
}
pub fn approved_certifications(&self)
-> impl Iterator<Item=&Signature> + Send + Sync
{
let mut hash_algo = None;
let digests: std::collections::HashSet<_> =
self.certification_approval_key_signatures()
.filter_map(|sig| {
sig.approved_certifications().ok()
.map(|digest_iter| (sig, digest_iter))
})
.flat_map(|(sig, digest_iter)| {
hash_algo = Some(sig.hash_algo());
digest_iter
})
.collect();
self.certifications()
.filter_map(move |sig| {
let mut hash = hash_algo.and_then(|a| a.context().ok())?
.for_signature(sig.version());
sig.hash_for_confirmation(&mut hash).ok()?;
let digest = hash.into_digest().ok()?;
if digests.contains(&digest[..]) {
Some(sig)
} else {
None
}
})
}
pub fn certification_approval_key_signatures(&'a self)
-> impl Iterator<Item=&'a Signature> + Send + Sync
{
let mut first = None;
self.ca.approvals()
.filter(move |sig| self.cert.policy().signature(
sig,
HashAlgoSecurity::CollisionResistance).is_ok())
.take_while(move |sig| {
let time_hash = (
if let Some(t) = sig.signature_creation_time() {
(t, sig.hash_algo())
} else {
return false;
},
sig.hash_algo());
if let Some(reference) = first {
reference == time_hash
} else {
first = Some(time_hash);
true
}
})
}
pub fn approve_of_certifications<C, S>(&self,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
self.ca
.approve_of_certifications(self.policy(),
self.time(),
primary_signer,
certifications)
}
}
impl<'a, C> Clone for ValidComponentAmalgamation<'a, C> {
fn clone(&self) -> Self {
Self {
ca: self.ca.clone(),
cert: self.cert.clone(),
binding_signature: self.binding_signature,
}
}
}
impl<'a, C: 'a> From<ValidComponentAmalgamation<'a, C>>
for ComponentAmalgamation<'a, C>
{
fn from(vca: ValidComponentAmalgamation<'a, C>) -> Self {
assert!(std::ptr::eq(vca.ca.cert(), vca.cert.cert()));
vca.ca
}
}
impl<'a, C> ValidComponentAmalgamation<'a, C>
where C: Ord + Send + Sync
{
pub(super) fn primary(cert: &'a Cert,
iter: std::slice::Iter<'a, ComponentBundle<C>>,
policy: &'a dyn Policy, t: SystemTime,
valid_cert: bool)
-> Result<ValidComponentAmalgamation<'a, C>>
{
use std::cmp::Ordering;
let mut error = None;
iter.filter_map(|c| {
let sig = match c.binding_signature(policy, t) {
Ok(sig) => Some(sig),
Err(e) => {
error = Some(e);
None
},
}?;
let revoked = c.revocation_status_intern(
policy, Some(t), false, Some(sig));
let primary = sig.primary_userid().unwrap_or(false);
let signature_creation_time = match sig.signature_creation_time() {
Some(time) => Some(time),
None => {
error = Some(Error::MalformedPacket(
"Signature has no creation time".into()).into());
None
},
}?;
Some(((c, sig, revoked), primary, signature_creation_time))
})
.max_by(|(a, a_primary, a_signature_creation_time),
(b, b_primary, b_signature_creation_time)| {
match (matches!(&a.2, RevocationStatus::Revoked(_)),
matches!(&b.2, RevocationStatus::Revoked(_))) {
(true, false) => return Ordering::Less,
(false, true) => return Ordering::Greater,
_ => (),
}
match (a_primary, b_primary) {
(true, false) => return Ordering::Greater,
(false, true) => return Ordering::Less,
_ => (),
}
match a_signature_creation_time.cmp(b_signature_creation_time)
{
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
Ordering::Equal => (),
}
match a.0.component().cmp(b.0.component()) {
Ordering::Less => Ordering::Greater,
Ordering::Greater => Ordering::Less,
Ordering::Equal =>
panic!("non-canonicalized Cert (duplicate components)"),
}
})
.ok_or_else(|| {
error.map(|e| e.context(format!(
"No binding signature at time {}", crate::fmt::time(&t))))
.unwrap_or_else(|| Error::NoBindingSignature(t).into())
})
.and_then(|c| ComponentAmalgamation::new(cert, (c.0).0)
.with_policy_relaxed(policy, t, valid_cert))
}
}
impl<'a, C> seal::Sealed for ValidComponentAmalgamation<'a, C> {}
impl<'a, C> ValidateAmalgamation<'a, C> for ValidComponentAmalgamation<'a, C> {
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.ca.cert(), self.cert.cert()));
let time = time.into().unwrap_or_else(crate::now);
self.ca.with_policy(policy, time)
}
}
impl<'a, C> ValidAmalgamation<'a, C> for ValidComponentAmalgamation<'a, C>
where
C: Send + Sync,
{
fn valid_cert(&self) -> &ValidCert<'a> {
assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
&self.cert
}
fn time(&self) -> SystemTime {
assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
self.cert.time
}
fn policy(&self) -> &'a dyn Policy
{
assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
self.cert.policy
}
fn binding_signature(&self) -> &'a Signature {
assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
self.binding_signature
}
fn revocation_status(&self) -> RevocationStatus<'a> {
self.bundle().revocation_status_intern(
self.policy(), Some(self.cert.time), false,
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, C> ValidBindingSignature<'a, C> for ValidComponentAmalgamation<'a, C>
where
C: Send + Sync,
{}
impl<'a, C> crate::cert::Preferences<'a>
for ValidComponentAmalgamation<'a, C>
where
C: Send + Sync,
{
fn preferred_symmetric_algorithms(&self)
-> Option<&'a [SymmetricAlgorithm]> {
self.map(|s| s.preferred_symmetric_algorithms())
}
fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]> {
self.map(|s| s.preferred_hash_algorithms())
}
fn preferred_compression_algorithms(&self)
-> Option<&'a [CompressionAlgorithm]> {
self.map(|s| s.preferred_compression_algorithms())
}
fn preferred_aead_ciphersuites(
&self)
-> Option<&'a [(SymmetricAlgorithm, AEADAlgorithm)]>
{
self.map(|s| s.preferred_aead_ciphersuites())
}
fn key_server_preferences(&self) -> Option<KeyServerPreferences> {
self.map(|s| s.key_server_preferences())
}
fn preferred_key_server(&self) -> Option<&'a [u8]> {
self.map(|s| s.preferred_key_server())
}
fn policy_uri(&self) -> Option<&'a [u8]> {
self.map(|s| s.policy_uri())
}
fn features(&self) -> Option<Features> {
self.map(|s| s.features())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::time::UNIX_EPOCH;
use crate::policy::StandardPolicy as P;
use crate::Packet;
use crate::packet::signature::SignatureBuilder;
use crate::packet::UserID;
use crate::types::SignatureType;
use crate::types::ReasonForRevocation;
#[test]
fn clone() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.add_userid("test@example.example")
.generate()
.unwrap();
let userid : UserIDAmalgamation = cert.userids().next().unwrap();
assert_eq!(userid.userid(), userid.clone().userid());
let userid : ValidUserIDAmalgamation
= userid.with_policy(p, None).unwrap();
let c = userid.clone();
assert_eq!(userid.userid(), c.userid());
assert_eq!(userid.time(), c.time());
}
#[test]
fn map() {
let (cert, _) = CertBuilder::new()
.add_userid("test@example.example")
.generate()
.unwrap();
let _ = cert.userids().map(|ua| ua.userid())
.collect::<Vec<_>>();
let _ = cert.user_attributes().map(|ua| ua.user_attribute())
.collect::<Vec<_>>();
}
#[test]
fn component_amalgamation_certifications_by_key() -> Result<()> {
let (alice, _) = CertBuilder::new()
.add_userid("<alice@example.example>")
.generate()
.unwrap();
let (bob, _) = CertBuilder::new()
.add_userid("<bob@example.example>")
.generate()
.unwrap();
let carol_userid = "<carol@example.example>";
let (carol, _) = CertBuilder::new()
.add_userid(carol_userid)
.generate()
.unwrap();
let ua = alice.userids().next().expect("have a user id");
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
let ua = bob.userids().next().expect("have a user id");
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
let mut alice_signer = alice.primary_key()
.key()
.clone()
.parts_into_secret().expect("have unencrypted key material")
.into_keypair().expect("have unencrypted key material");
let certification = SignatureBuilder::new(SignatureType::GenericCertification)
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification)?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 1);
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).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::GenericCertification)
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification)?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 2);
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).count(), 1);
let certification = SignatureBuilder::new(SignatureType::GenericCertification)
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification)?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 3);
assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).count(), 2);
Ok(())
}
#[test]
fn user_id_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 ua = alice.userids().next().expect("have a user id");
assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, None, alice_primary).count(), 0);
let ua = bob.userids().next().expect("have a user id");
assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, None, alice_primary).count(), 0);
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
assert_eq!(ua.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::GenericCertification)
.set_signature_creation_time(t1)?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification.clone())?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 1);
assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 0);
assert_eq!(ua.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::GenericCertification)
.set_signature_creation_time(t1)?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification.clone())?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.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::GenericCertification)
.set_signature_creation_time(t2)?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification.clone())?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 3);
assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.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::GenericCertification)
.set_signature_creation_time(t1)?
.set_signature_validity_period(t2.duration_since(t1)?)?
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification.clone())?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 4);
assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 1);
assert_eq!(ua.active_certifications_by_key(p, t1, bob_primary).count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t2, bob_primary).count(), 0);
assert_eq!(ua.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::GenericCertification)
.set_signature_creation_time(t1)?
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&UserID::from(carol_userid))?;
let carol = carol.insert_packets(certification.clone())?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.certifications().count(), 5);
assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 2);
assert_eq!(ua.active_certifications_by_key(p, t1, bob_primary).count(), 2);
assert_eq!(ua.valid_certifications_by_key(p, t2, bob_primary).count(), 1);
assert_eq!(ua.active_certifications_by_key(p, t2, bob_primary).count(), 1);
Ok(())
}
#[test]
fn user_id_amalgamation_third_party_revocations_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 carol_userid = UserID::from(carol_userid);
let ua = alice.userids().next().expect("have a user id");
assert_eq!(ua.valid_third_party_revocations_by_key(p, None, alice_primary).count(), 0);
let ua = bob.userids().next().expect("have a user id");
assert_eq!(ua.valid_third_party_revocations_by_key(p, None, alice_primary).count(), 0);
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.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 certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(
ReasonForRevocation::UIDRetired, b"")?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&carol_userid)?;
let carol = carol.insert_packets([
Packet::from(carol_userid.clone()),
Packet::from(certification.clone()),
])?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.other_revocations().count(), 1);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 1);
assert_eq!(ua.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 certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&carol_userid)?;
let carol = carol.insert_packets([
Packet::from(carol_userid.clone()),
Packet::from(certification.clone()),
])?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.other_revocations().count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 2);
assert_eq!(ua.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 certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
.set_signature_creation_time(t2)?
.set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
.sign_userid_binding(
&mut alice_signer,
carol.primary_key().key(),
&carol_userid)?;
let carol = carol.insert_packets([
Packet::from(carol_userid.clone()),
Packet::from(certification.clone()),
])?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.other_revocations().count(), 3);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.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 certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
.set_signature_creation_time(t1)?
.set_signature_validity_period(t2.duration_since(t1)?)?
.set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&carol_userid)?;
let carol = carol.insert_packets([
Packet::from(carol_userid.clone()),
Packet::from(certification.clone()),
])?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.other_revocations().count(), 4);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, bob_primary).count(), 1);
assert_eq!(ua.valid_third_party_revocations_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::CertificationRevocation)
.set_signature_creation_time(t1)?
.set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
.sign_userid_binding(
&mut bob_signer,
carol.primary_key().key(),
&carol_userid)?;
let carol = carol.insert_packets([
Packet::from(carol_userid.clone()),
Packet::from(certification.clone()),
])?.0;
let ua = carol.userids().next().expect("have a user id");
assert_eq!(ua.other_revocations().count(), 5);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, bob_primary).count(), 2);
assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, bob_primary).count(), 1);
Ok(())
}
}