use super::CheckedPathCertificate;
use crate::timestamp::{Interval, Role, TemporalError, Verified};
use crate::xml::{Access, AccessError, Xml};
use structured_asn1::memory::Work;
use structured_pkix::temporal::LeapTable;
pub struct SignatureTime<'receipt, 'view, 'document, N, D, G> {
certificate: &'receipt CheckedPathCertificate<'view, 'document, N, D, G>,
timestamp: &'receipt Verified<N, D, G>,
generation: Interval,
}
impl<'receipt, 'view, 'document, N, D, G> SignatureTime<'receipt, 'view, 'document, N, D, G> {
#[must_use]
pub fn certificate(&self) -> &'receipt CheckedPathCertificate<'view, 'document, N, D, G> {
self.certificate
}
#[must_use]
pub fn timestamp(&self) -> &'receipt Verified<N, D, G> {
self.timestamp
}
#[must_use]
pub fn generation(&self) -> Interval {
self.generation
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignatureTimeFailure {
Certificate,
Interval,
}
#[derive(Debug)]
pub enum SignatureTimeError<E, N> {
Access(AccessError<E, N>),
Foreign {
property: N,
},
Role {
property: N,
},
Time {
property: N,
cause: TemporalError,
},
}
pub enum SignatureTimeBinding<'receipt, 'view, 'document, N, D, G> {
Bound(SignatureTime<'receipt, 'view, 'document, N, D, G>),
Unproven(SignatureTimeFailure),
}
impl<'view, 'document, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq>
CheckedPathCertificate<'view, 'document, N, D, G>
{
pub fn bind_signature_time<'receipt, X: Xml<Id = N, Document = D, Generation = G>, W: Work>(
&'receipt self,
timestamp: &'receipt Verified<N, D, G>,
utc: &LeapTable<'_>,
access: &Access<'_, X, W>,
) -> Result<
SignatureTimeBinding<'receipt, 'view, 'document, N, D, G>,
SignatureTimeError<X::Error, N>,
> {
self.bind_generation(timestamp, access, || {
timestamp.interval(utc, access.execution())
})
}
pub fn bind_signature_time_path<
'receipt,
X: Xml<Id = N, Document = D, Generation = G>,
W: Work,
>(
&'receipt self,
path: &'receipt crate::pki::TimestampPath<'_, '_, '_, N, D, G>,
access: &Access<'_, X, W>,
) -> Result<
SignatureTimeBinding<'receipt, 'view, 'document, N, D, G>,
SignatureTimeError<X::Error, N>,
> {
self.bind_generation(path.actor().verified(), access, || Ok(path.generation()))
}
fn bind_generation<'receipt, X: Xml<Id = N, Document = D, Generation = G>, W: Work>(
&'receipt self,
timestamp: &'receipt Verified<N, D, G>,
access: &Access<'_, X, W>,
interval: impl FnOnce() -> Result<Interval, TemporalError>,
) -> Result<
SignatureTimeBinding<'receipt, 'view, 'document, N, D, G>,
SignatureTimeError<X::Error, N>,
> {
let references = self.selected().reference();
if references.snapshot() != access.snapshot()
|| timestamp.token().snapshot != references.snapshot()
{
return Err(SignatureTimeError::Foreign {
property: timestamp.property(),
});
}
if !access
.equal_id(timestamp.signature(), references.signature())
.map_err(|cause| {
SignatureTimeError::Access(AccessError::Resource {
node: timestamp.property(),
cause,
})
})?
{
return Err(SignatureTimeError::Foreign {
property: timestamp.property(),
});
}
access
.query(references.signature(), |_, _, _| Ok(()))
.map_err(SignatureTimeError::Access)?;
if timestamp.role() != Role::Signature {
return Err(SignatureTimeError::Role {
property: timestamp.property(),
});
}
if !self.is_bound() {
return Ok(SignatureTimeBinding::Unproven(
SignatureTimeFailure::Certificate,
));
}
let generation = interval().map_err(|cause| SignatureTimeError::Time {
property: timestamp.property(),
cause,
})?;
access
.query(references.signature(), |_, _, _| Ok(()))
.map_err(SignatureTimeError::Access)?;
let checked = self.digests().path().interval();
if checked.from_instant() > generation.earliest || checked.to_instant() < generation.latest
{
return Ok(SignatureTimeBinding::Unproven(
SignatureTimeFailure::Interval,
));
}
Ok(SignatureTimeBinding::Bound(SignatureTime {
certificate: self,
timestamp,
generation,
}))
}
}