use std::time;
use std::time::SystemTime;
use std::clone::Clone;
use std::borrow::Borrow;
use crate::{
cert::prelude::*,
crypto::{Signer, hash::{Hash, Digest}},
Error,
packet::{
Signature,
Unknown,
UserAttribute,
UserID,
},
Result,
policy::{
HashAlgoSecurity,
Policy,
},
seal,
types::{
AEADAlgorithm,
CompressionAlgorithm,
Features,
HashAlgorithm,
KeyServerPreferences,
RevocationKey,
RevocationStatus,
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 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))
}
fn 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.cert().cert.primary.binding_signature(self.policy(), self.time())
}
fn revocation_status(&self) -> RevocationStatus<'a>;
fn revocation_keys(&self)
-> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>;
}
#[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> std::ops::Deref for ComponentAmalgamation<'a, C> {
type Target = ComponentBundle<C>;
fn deref(&self) -> &Self::Target {
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
}
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().iter()
}
pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().certifications().iter()
}
pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().self_revocations().iter()
}
pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
self.bundle().other_revocations().iter()
}
pub fn signatures(&self)
-> impl Iterator<Item = &'a Signature> + Send + Sync {
self.bundle().signatures()
}
}
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,
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 attest_certifications<C, S>(&self,
policy: &dyn Policy,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
let mut hash = HashAlgorithm::default().context()?;
self.cert().primary_key().hash(&mut hash);
self.userid().hash(&mut hash);
let old = self.clone()
.with_policy(policy, None)
.ok()
.and_then(|v| v.attestation_key_signatures().next().cloned());
attest_certifications_common(hash, old, primary_signer, certifications)
}
}
impl<'a> UserAttributeAmalgamation<'a> {
pub fn user_attribute(&self) -> &'a UserAttribute {
self.component()
}
pub fn attest_certifications<C, S>(&self,
policy: &dyn Policy,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
let mut hash = HashAlgorithm::default().context()?;
self.cert().primary_key().hash(&mut hash);
self.user_attribute().hash(&mut hash);
let old = self.clone()
.with_policy(policy, None)
.ok()
.and_then(|v| v.attestation_key_signatures().next().cloned());
attest_certifications_common(hash, old, primary_signer, certifications)
}
}
#[allow(clippy::let_and_return)]
fn attest_certifications_common<C, S>(hash: Box<dyn Digest>,
old_attestation: Option<Signature>,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
use crate::{
packet::signature::{SignatureBuilder, subpacket::SubpacketArea},
serialize::MarshalInto,
};
let hash_algo = hash.algo();
let digest_size = hash.digest_size();
let mut attestations = Vec::new();
for certification in certifications.into_iter() {
let mut h = hash_algo.context()?;
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 = old.into();
s.hashed_area_mut().clear();
s.unhashed_area_mut().clear();
s
} else {
use crate::packet::signature::SIG_BACKDATE_BY;
let creation_time =
crate::now() - time::Duration::new(SIG_BACKDATE_BY, 0);
let template = SignatureBuilder::new(SignatureType::AttestationKey)
.set_signature_creation_time(creation_time)?;
template
};
let template = template
.set_hash_algo(hash_algo)
.pre_sign(primary_signer)?;
let available_space =
SubpacketArea::MAX_SIZE - template.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) {
sigs.push(
template.clone()
.set_attested_certifications(digests)?
.sign_hash(primary_signer, hash.clone())?);
}
if attestations.is_empty() {
assert!(sigs.is_empty());
sigs.push(
template
.set_attested_certifications(Option::<&[u8]>::None)?
.sign_hash(primary_signer, hash.clone())?);
}
Ok(sigs)
}
#[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);
pub type ValidUserIDAmalgamation<'a> = ValidComponentAmalgamation<'a, UserID>;
impl<'a> ValidUserIDAmalgamation<'a> {
pub fn attested_certifications(&self)
-> impl Iterator<Item=&Signature> + Send + Sync
{
let mut hash_algo = None;
let digests: std::collections::HashSet<_> =
self.attestation_key_signatures()
.filter_map(|sig| {
sig.attested_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())?;
sig.hash_for_confirmation(&mut hash);
let digest = hash.into_digest().ok()?;
if digests.contains(&digest[..]) {
Some(sig)
} else {
None
}
})
}
pub fn attestation_key_signatures(&'a self)
-> impl Iterator<Item=&'a Signature> + Send + Sync
{
let mut first = None;
self.attestations()
.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 attest_certifications<C, S>(&self,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
std::ops::Deref::deref(self)
.attest_certifications(self.policy(),
primary_signer,
certifications)
}
}
pub type ValidUserAttributeAmalgamation<'a>
= ValidComponentAmalgamation<'a, UserAttribute>;
impl<'a> ValidUserAttributeAmalgamation<'a> {
pub fn attested_certifications(&self)
-> impl Iterator<Item=&Signature> + Send + Sync
{
let mut hash_algo = None;
let digests: std::collections::HashSet<_> =
self.attestation_key_signatures()
.filter_map(|sig| {
sig.attested_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())?;
sig.hash_for_confirmation(&mut hash);
let digest = hash.into_digest().ok()?;
if digests.contains(&digest[..]) {
Some(sig)
} else {
None
}
})
}
pub fn attestation_key_signatures(&'a self)
-> impl Iterator<Item=&'a Signature> + Send + Sync
{
let mut first = None;
self.attestations()
.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 attest_certifications<C, S>(&self,
primary_signer: &mut dyn Signer,
certifications: C)
-> Result<Vec<Signature>>
where C: IntoIterator<Item = S>,
S: Borrow<Signature>,
{
std::ops::Deref::deref(self)
.attest_certifications(self.policy(),
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> std::ops::Deref for ValidComponentAmalgamation<'a, C> {
type Target = ComponentAmalgamation<'a, C>;
fn deref(&self) -> &Self::Target {
assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
&self.ca
}
}
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(policy, 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))
}
pub fn self_signatures(&self) -> impl Iterator<Item=&Signature> + Send + Sync {
std::ops::Deref::deref(self).self_signatures()
.filter(move |sig| self.cert.policy().signature(sig,
self.hash_algo_security).is_ok())
}
pub fn certifications(&self) -> impl Iterator<Item=&Signature> + Send + Sync {
std::ops::Deref::deref(self).certifications()
.filter(move |sig| self.cert.policy().signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
pub fn self_revocations(&self) -> impl Iterator<Item=&Signature> + Send + Sync {
std::ops::Deref::deref(self).self_revocations()
.filter(move |sig|self.cert.policy().signature(sig,
self.hash_algo_security).is_ok())
}
pub fn other_revocations(&self) -> impl Iterator<Item=&Signature> + Send + Sync {
std::ops::Deref::deref(self).other_revocations()
.filter(move |sig| self.cert.policy().signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
pub fn signatures(&self)
-> impl Iterator<Item = &Signature> + Send + Sync {
std::ops::Deref::deref(self).signatures()
.filter(move |sig| self.cert.policy().signature(sig,
HashAlgoSecurity::CollisionResistance).is_ok())
}
}
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> {
fn 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(self.policy(), 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().hash_algo_security();
let sec = self.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> crate::cert::Preferences<'a>
for ValidComponentAmalgamation<'a, C>
{
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_algorithms(&self) -> Option<&'a [AEADAlgorithm]> {
self.map(|s| s.preferred_aead_algorithms())
}
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 crate::policy::StandardPolicy as P;
use crate::cert::prelude::*;
#[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<_>>();
}
}