use super::{Input, InputError, InputResult, Role, Stamp, TimestampXml};
use crate::xml::{Access, Snapshot, Xml};
use crate::{binding, container, pki::CounterPaths, properties};
use structured_asn1::memory::{OwnedVec, Work};
pub struct CounterStamp<N, D, G> {
layout: container::Containers<N, D, G>,
stamp: Stamp<N, D, G>,
}
impl<N: Copy, D: Copy, G: Copy> CounterStamp<N, D, G> {
#[must_use]
pub fn stamp(&self) -> &Stamp<N, D, G> {
&self.stamp
}
pub fn input<'xml, X: TimestampXml<Id = N, Document = D, Generation = G>, W: Work>(
&self,
xml: &'xml X,
execution: &W,
) -> InputResult<Input<'xml, X>, X> {
Input::prepare_in(xml, &self.layout, &self.stamp, execution)
}
}
pub struct CounterTimestamps<N, D, G> {
pub(super) snapshot: Snapshot<D, G>,
pub(super) signature: N,
pub(super) entries: OwnedVec<CounterStamp<N, D, G>>,
pub(super) signatures: OwnedVec<N>,
}
impl<N, D, G> CounterTimestamps<N, D, G> {
#[must_use]
pub fn entries(&self) -> &[CounterStamp<N, D, G>] {
&self.entries
}
}
#[derive(Debug)]
pub enum CounterTimestampError<E, N> {
Input(InputError<E, N>),
Property(properties::Error<E, N>),
Binding(binding::BindingError<E, N>),
}
pub type CounterTimestampResult<X> = Result<
CounterTimestamps<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
CounterTimestampError<<X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn counter_timestamps<X: binding::SignatureXml, W: Work>(
access: &Access<'_, X, W>,
counters: &CounterPaths<'_, '_, X::Id, X::Document, X::Generation>,
) -> CounterTimestampResult<X> {
let root = counters.signature();
if counters.snapshot() != access.snapshot() {
return Err(CounterTimestampError::Input(InputError::Foreign {
node: root,
}));
}
let mut entries = access.execution().vector();
let mut signatures = access.execution().vector();
for index in 0..counters.actors().len() {
pay(access, root)?;
let actor = &counters.actors()[index];
let signature = actor.signer().counter().original().child();
pay(access, signature)?;
signatures.try_push(signature).map_err(|cause| {
CounterTimestampError::Input(InputError::Resource {
node: signature,
cause,
})
})?;
let Some(layout) = actor.containers() else {
continue;
};
let [parent, _] = crate::unsigned::containers(access, layout.unsigned())
.map_err(CounterTimestampError::Property)?;
if let Some(parent) = parent {
let mut target_checked = false;
for child in access.children(parent) {
let node = child
.map_err(|cause| CounterTimestampError::Input(InputError::Child(cause)))?;
if super::named(access, node, container::XADES, "SignatureTimeStamp")
.map_err(CounterTimestampError::Input)?
|| super::named(access, node, super::XADES_141, "ArchiveTimeStamp")
.map_err(CounterTimestampError::Input)?
|| super::named(access, node, super::XADES_141, "SigAndRefsTimeStampV2")
.map_err(CounterTimestampError::Input)?
|| super::named(access, node, super::XADES_141, "RefsOnlyTimeStampV2")
.map_err(CounterTimestampError::Input)?
{
if !target_checked {
binding::check_target(access, layout)
.map_err(CounterTimestampError::Binding)?;
target_checked = true;
}
let stamp = super::read_in(access, layout, node)
.map_err(CounterTimestampError::Input)?;
entries
.try_push(CounterStamp {
layout: *layout,
stamp,
})
.map_err(|cause| {
CounterTimestampError::Input(InputError::Resource { node, cause })
})?;
}
}
}
let (_, objects) =
properties::sections(access, layout).map_err(CounterTimestampError::Property)?;
if let Some(parent) = objects {
let objects = properties::read_objects(access, parent)
.map_err(CounterTimestampError::Property)?;
let mut authenticated = false;
for entry in objects.entries() {
pay(access, entry.node)?;
let role = match entry.role {
properties::DataObjectRole::AllTimestamp => Role::AllDataObjects,
properties::DataObjectRole::IndividualTimestamp => Role::IndividualDataObjects,
_ => continue,
};
if !authenticated {
binding::bind(access, layout).map_err(CounterTimestampError::Binding)?;
authenticated = true;
}
let stamp = super::read_object_fields(access, layout, &objects, entry.node, role)
.map_err(CounterTimestampError::Input)?;
entries
.try_push(CounterStamp {
layout: *layout,
stamp,
})
.map_err(|cause| {
CounterTimestampError::Input(InputError::Resource {
node: entry.node,
cause,
})
})?;
}
}
}
pay(access, root)?;
Ok(CounterTimestamps {
snapshot: access.snapshot(),
signature: root,
entries: entries.into_owned(),
signatures: signatures.into_owned(),
})
}
fn pay<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
) -> Result<(), CounterTimestampError<X::Error, X::Id>> {
access
.query(node, |_, _, work| Ok(work.charge_work(1)))
.map_err(|cause| CounterTimestampError::Input(InputError::Access(cause)))?
.map_err(|cause| CounterTimestampError::Input(InputError::Resource { node, cause }))
}