Skip to main content

structured_xades/timestamp/
counters.rs

1//! Timestamp inputs from the complete, original countersignature census.
2
3use super::{Input, InputError, InputResult, Role, Stamp, TimestampXml};
4use crate::xml::{Access, Snapshot, Xml};
5use crate::{binding, container, pki::CounterPaths, properties};
6use structured_asn1::memory::{OwnedVec, Work};
7
8/// One child timestamp and its original input scope. Construction requires the
9/// actual counter receipt; a root binding cannot stand in for this child.
10pub struct CounterStamp<N, D, G> {
11    layout: container::Containers<N, D, G>,
12    stamp: Stamp<N, D, G>,
13}
14
15impl<N: Copy, D: Copy, G: Copy> CounterStamp<N, D, G> {
16    /// Original property, including all of its encapsulated tokens.
17    #[must_use]
18    pub fn stamp(&self) -> &Stamp<N, D, G> {
19        &self.stamp
20    }
21
22    /// Prepare the child's original `SignatureValue`, reference outputs or archive
23    /// components through the SAME selector as the root. No signature is replayed.
24    pub fn input<'xml, X: TimestampXml<Id = N, Document = D, Generation = G>, W: Work>(
25        &self,
26        xml: &'xml X,
27        execution: &W,
28    ) -> InputResult<Input<'xml, X>, X> {
29        Input::prepare_in(xml, &self.layout, &self.stamp, execution)
30    }
31}
32
33/// Complete child timestamp census, including nested children in counter order.
34/// This is source selection, not positive timestamp or baseline evidence.
35pub struct CounterTimestamps<N, D, G> {
36    pub(super) snapshot: Snapshot<D, G>,
37    pub(super) signature: N,
38    pub(super) entries: OwnedVec<CounterStamp<N, D, G>>,
39    pub(super) signatures: OwnedVec<N>,
40}
41
42impl<N, D, G> CounterTimestamps<N, D, G> {
43    /// Every original child timestamp; failed/unreadable tokens remain obligations.
44    #[must_use]
45    pub fn entries(&self) -> &[CounterStamp<N, D, G>] {
46        &self.entries
47    }
48}
49
50/// Original child field, authenticated signed-property, or admission refusal.
51#[derive(Debug)]
52pub enum CounterTimestampError<E, N> {
53    /// Current original source/input refusal.
54    Input(InputError<E, N>),
55    /// Original signed-property structural failure.
56    Property(properties::Error<E, N>),
57    /// Signed object timestamps lack whole original `SignedProperties` coverage.
58    Binding(binding::BindingError<E, N>),
59}
60
61/// Complete child source census or the original field/binding/admission cause.
62pub type CounterTimestampResult<X> = Result<
63    CounterTimestamps<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
64    CounterTimestampError<<X as Xml>::Error, <X as Xml>::Id>,
65>;
66
67/// Read all original child timestamp properties without imposing the root's
68/// SigningTime/SigningCertificateV2/header requirements on a plain counter.
69/// Signed object timestamps do require actual whole-property authentication;
70/// unsigned timestamps need the actual checked child, direct layout and Target
71/// association with that child; no signed-property reference is fabricated.
72/// Partial output is dropped on any source, schema, binding or resource refusal.
73pub fn counter_timestamps<X: binding::SignatureXml, W: Work>(
74    access: &Access<'_, X, W>,
75    counters: &CounterPaths<'_, '_, X::Id, X::Document, X::Generation>,
76) -> CounterTimestampResult<X> {
77    let root = counters.signature();
78    if counters.snapshot() != access.snapshot() {
79        return Err(CounterTimestampError::Input(InputError::Foreign {
80            node: root,
81        }));
82    }
83    let mut entries = access.execution().vector();
84    let mut signatures = access.execution().vector();
85    for index in 0..counters.actors().len() {
86        pay(access, root)?;
87        let actor = &counters.actors()[index];
88        let signature = actor.signer().counter().original().child();
89        pay(access, signature)?;
90        signatures.try_push(signature).map_err(|cause| {
91            CounterTimestampError::Input(InputError::Resource {
92                node: signature,
93                cause,
94            })
95        })?;
96        let Some(layout) = actor.containers() else {
97            continue;
98        };
99        let [parent, _] = crate::unsigned::containers(access, layout.unsigned())
100            .map_err(CounterTimestampError::Property)?;
101        if let Some(parent) = parent {
102            let mut target_checked = false;
103            for child in access.children(parent) {
104                let node = child
105                    .map_err(|cause| CounterTimestampError::Input(InputError::Child(cause)))?;
106                if super::named(access, node, container::XADES, "SignatureTimeStamp")
107                    .map_err(CounterTimestampError::Input)?
108                    || super::named(access, node, super::XADES_141, "ArchiveTimeStamp")
109                        .map_err(CounterTimestampError::Input)?
110                    || super::named(access, node, super::XADES_141, "SigAndRefsTimeStampV2")
111                        .map_err(CounterTimestampError::Input)?
112                    || super::named(access, node, super::XADES_141, "RefsOnlyTimeStampV2")
113                        .map_err(CounterTimestampError::Input)?
114                {
115                    if !target_checked {
116                        binding::check_target(access, layout)
117                            .map_err(CounterTimestampError::Binding)?;
118                        target_checked = true;
119                    }
120                    let stamp = super::read_in(access, layout, node)
121                        .map_err(CounterTimestampError::Input)?;
122                    entries
123                        .try_push(CounterStamp {
124                            layout: *layout,
125                            stamp,
126                        })
127                        .map_err(|cause| {
128                            CounterTimestampError::Input(InputError::Resource { node, cause })
129                        })?;
130                }
131            }
132        }
133        let (_, objects) =
134            properties::sections(access, layout).map_err(CounterTimestampError::Property)?;
135        if let Some(parent) = objects {
136            let objects = properties::read_objects(access, parent)
137                .map_err(CounterTimestampError::Property)?;
138            let mut authenticated = false;
139            for entry in objects.entries() {
140                pay(access, entry.node)?;
141                let role = match entry.role {
142                    properties::DataObjectRole::AllTimestamp => Role::AllDataObjects,
143                    properties::DataObjectRole::IndividualTimestamp => Role::IndividualDataObjects,
144                    _ => continue,
145                };
146                if !authenticated {
147                    binding::bind(access, layout).map_err(CounterTimestampError::Binding)?;
148                    authenticated = true;
149                }
150                let stamp = super::read_object_fields(access, layout, &objects, entry.node, role)
151                    .map_err(CounterTimestampError::Input)?;
152                entries
153                    .try_push(CounterStamp {
154                        layout: *layout,
155                        stamp,
156                    })
157                    .map_err(|cause| {
158                        CounterTimestampError::Input(InputError::Resource {
159                            node: entry.node,
160                            cause,
161                        })
162                    })?;
163            }
164        }
165    }
166    pay(access, root)?;
167    Ok(CounterTimestamps {
168        snapshot: access.snapshot(),
169        signature: root,
170        entries: entries.into_owned(),
171        signatures: signatures.into_owned(),
172    })
173}
174
175fn pay<X: Xml, W: Work>(
176    access: &Access<'_, X, W>,
177    node: X::Id,
178) -> Result<(), CounterTimestampError<X::Error, X::Id>> {
179    access
180        .query(node, |_, _, work| Ok(work.charge_work(1)))
181        .map_err(|cause| CounterTimestampError::Input(InputError::Access(cause)))?
182        .map_err(|cause| CounterTimestampError::Input(InputError::Resource { node, cause }))
183}