Skip to main content

structured_xades/
prepare.rs

1//! One original-source preparation path for subsequent baseline execution.
2
3use crate::reference::ReferenceXml;
4use crate::xml::{Access, AccessError, Xml};
5use crate::{
6    binding, certificate, commitment, container, counter, format, header, material, policy,
7    properties, renewed, role, timestamp, unsigned, validation,
8};
9use structured_asn1::memory::{MemoryError, OwnedVec, Work};
10use structured_ltv::baseline::ArchiveCensus;
11use structured_ltv::source::Location;
12
13mod uri;
14pub use uri::{UriError, UriResult, check_uris};
15mod fields;
16pub use fields::{FieldError, FieldResult, Fields, check_fields};
17
18/// Original refusal in the corresponding processing stage.
19#[derive(Debug)]
20pub enum Error<E, N> {
21    /// Original direct-incorporation layout failure.
22    Container(container::ContainerError<E, N>),
23    /// Selected XMLDSig/whole-SignedProperties binding failure.
24    Binding(binding::BindingError<E, N>),
25    /// Original property/header/material structure failure.
26    Property(properties::Error<E, N>),
27    /// Original complete object-format/reference association failure.
28    Format(format::CoverageError<E, N>),
29    /// Original signed commitment schema/reference-association refusal.
30    Commitment(commitment::AssociationError<E, N>),
31    /// Original timestamp selection failure.
32    Timestamp(timestamp::InputError<E, N>),
33    /// Original validation-data selection failure.
34    Validation(validation::Error<E, N>),
35    /// Original source/dispatch admission refusal.
36    Access(AccessError<E, N>),
37    /// Accounted metadata catalogue admission refusal.
38    Resource {
39        /// Original selected signature.
40        signature: N,
41        /// Original resource cause.
42        cause: MemoryError,
43    },
44}
45
46/// Original prepared source models. This is NOT a verified B level: scalar/
47/// optional-property semantics, certificate/key/path/status checking and actual
48/// timestamp execution still have to complete. No trust/evidence constructor is
49/// exposed by this object, and unknown original properties remain in inventory.
50pub struct Preparation<N, D, G> {
51    requirements: unsigned::Requirements,
52    bound: binding::Bound<N, D, G>,
53    signed: properties::Signed<N, D, G>,
54    header: header::Header<N, D, G>,
55    certificates: certificate::References<N, D, G>,
56    formats: OwnedVec<format::CoveredFormat<N, D, G>>,
57    commitments: OwnedVec<commitment::Association<N, D, G>>,
58    roles: Option<role::Roles<N, D, G>>,
59    policy: Option<policy::Reading<N, D, G>>,
60    policy_store: Option<policy::Store<N, D, G>>,
61    unsigned: unsigned::Catalogue<N, D, G>,
62    timestamps: OwnedVec<timestamp::Stamp<N, D, G>>,
63    material: OwnedVec<material::Catalogue<N, D, G>>,
64    validation: OwnedVec<validation::Data<N, D, G>>,
65    renewed: OwnedVec<renewed::Catalogue<N, D, G>>,
66    references: OwnedVec<validation::ReferenceProperty<N, D, G>>,
67}
68
69impl<N, D, G> Preparation<N, D, G> {
70    /// Requested structural requirements actually enforced during preparation;
71    /// this does not infer a verified level from additional container presence.
72    #[must_use]
73    pub fn requirements(&self) -> unsigned::Requirements {
74        self.requirements
75    }
76    /// Original authenticated whole-SignedProperties binding.
77    #[must_use]
78    pub fn bound(&self) -> &binding::Bound<N, D, G> {
79        &self.bound
80    }
81    /// Original signed-property roles, including unprocessed optional fields.
82    #[must_use]
83    pub fn signed(&self) -> &properties::Signed<N, D, G> {
84        &self.signed
85    }
86    /// Baseline header roles, before candidate inclusion checking.
87    #[must_use]
88    pub fn header(&self) -> &header::Header<N, D, G> {
89        &self.header
90    }
91    /// Original certificate-digest references, before actual digest/key binding.
92    #[must_use]
93    pub fn certificates(&self) -> &certificate::References<N, D, G> {
94        &self.certificates
95    }
96    /// Complete required reference association and exact ds:Object metadata
97    /// agreement and MIME syntax; URI lexical validation remains separate.
98    #[must_use]
99    pub fn formats(&self) -> &[format::CoveredFormat<N, D, G>] {
100        &self.formats
101    }
102    /// Original commitments with completed authenticated instruction lookup;
103    /// URI lexical and application-policy meaning checks remain separate.
104    #[must_use]
105    pub fn commitments(&self) -> &[commitment::Association<N, D, G>] {
106        &self.commitments
107    }
108    /// Original optional role credentials, without issuer trust assertions.
109    #[must_use]
110    pub fn roles(&self) -> Option<&role::Roles<N, D, G>> {
111        self.roles.as_ref()
112    }
113    /// Original signed policy choice, before document or application acceptance.
114    #[must_use]
115    pub fn policy(&self) -> Option<&policy::Reading<N, D, G>> {
116        self.policy.as_ref()
117    }
118    /// Original store permitted by the signed explicit policy hash. Its bytes
119    /// remain untrusted until actual comparison with that signed digest.
120    #[must_use]
121    pub fn policy_store(&self) -> Option<&policy::Store<N, D, G>> {
122        self.policy_store.as_ref()
123    }
124    /// Full original unsigned-property inventory; it is not a material census.
125    #[must_use]
126    pub fn unsigned(&self) -> &unsigned::Catalogue<N, D, G> {
127        &self.unsigned
128    }
129    /// Selected timestamp source models, not parsed or verified token evidence.
130    #[must_use]
131    pub fn timestamps(&self) -> &[timestamp::Stamp<N, D, G>] {
132        &self.timestamps
133    }
134    /// Original ordinary/attribute validation-material value catalogues.
135    #[must_use]
136    pub fn material(&self) -> &[material::Catalogue<N, D, G>] {
137        &self.material
138    }
139    /// Original general/timestamp-specific validation-data catalogues.
140    #[must_use]
141    pub fn validation(&self) -> &[validation::Data<N, D, G>] {
142        &self.validation
143    }
144    /// Original renewed-digest pairs. Authenticated detached Manifest matching
145    /// and both actual hash comparisons remain separate execution obligations.
146    #[must_use]
147    pub fn renewed(&self) -> &[renewed::Catalogue<N, D, G>] {
148        &self.renewed
149    }
150    /// Complete original optional validation-reference fields. These unsigned
151    /// models cannot substitute for protected signing-certificate binding;
152    /// their scalar syntax and actual artifact comparisons remain separate.
153    #[must_use]
154    pub fn validation_references(&self) -> &[validation::ReferenceProperty<N, D, G>] {
155        &self.references
156    }
157}
158
159impl<N: Copy, D: Copy, G: Copy> ArchiveCensus for Preparation<N, D, G> {
160    type Id = N;
161    type Document = D;
162    type Generation = G;
163
164    fn signature(&self) -> Location<N, D, G> {
165        let layout = self.bound.containers();
166        Location {
167            snapshot: layout.snapshot(),
168            node: layout.signature(),
169        }
170    }
171    fn timestamps(&self) -> usize {
172        self.timestamps.len()
173    }
174    fn archive(&self, ordinal: usize) -> Option<N> {
175        let stamp = &self.timestamps[ordinal];
176        (stamp.role() == timestamp::Role::Archive).then(|| stamp.node())
177    }
178}
179
180/// Preparation using this owner's stable identity domain. Retained metadata
181/// does not pin the XML adapter type or its temporary scoped view lifetime.
182pub type Prepared<X> = Preparation<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
183
184/// Source preparation result retaining stage-specific original causes.
185pub type PrepareResult<T, X> = Result<T, Error<<X as Xml>::Error, <X as Xml>::Id>>;
186
187/// Prepares the original selected signature with one shared structural path.
188/// No certificate hash, canonicalization, token/provider execution or implicit
189/// acquisition occurs here. All catalogues retain their actual capacity account;
190/// refusal drops partial preparation without returning a successful subset.
191pub fn prepare<X: ReferenceXml, W: Work>(
192    access: &Access<'_, X, W>,
193    signature: X::Id,
194    requirements: unsigned::Requirements,
195    counter: Option<&counter::Binding<X>>,
196) -> PrepareResult<Prepared<X>, X> {
197    let layout = container::locate(access, signature).map_err(Error::Container)?;
198    let bound = binding::bind(access, &layout).map_err(Error::Binding)?;
199    let header = header::read(access, &bound).map_err(Error::Property)?;
200    let signed = properties::read(access, &bound).map_err(Error::Property)?;
201    let certificates = certificate::read(access, &signed).map_err(Error::Property)?;
202    let roles = role::read(access, &signed).map_err(Error::Property)?;
203    let policy = policy::read(access, &signed).map_err(Error::Property)?;
204    let unsigned = unsigned::read(access, &bound, requirements).map_err(Error::Property)?;
205    let policy_store =
206        policy::read_store(access, &unsigned, policy.as_ref()).map_err(Error::Property)?;
207    unsigned::check_credential_material(access, &unsigned, roles.as_ref())
208        .map_err(Error::Property)?;
209    let formats =
210        format::check_references(access, &bound, &signed, counter).map_err(Error::Format)?;
211    let mut commitments = access
212        .query(signature, |_, _, work| Ok(work.vector()))
213        .map_err(Error::Access)?;
214    let mut timestamps = access
215        .query(signature, |_, _, work| Ok(work.vector()))
216        .map_err(Error::Access)?;
217    let mut material = access
218        .query(signature, |_, _, work| Ok(work.vector()))
219        .map_err(Error::Access)?;
220    let mut validation = access
221        .query(signature, |_, _, work| Ok(work.vector()))
222        .map_err(Error::Access)?;
223    let mut renewed = access
224        .query(signature, |_, _, work| Ok(work.vector()))
225        .map_err(Error::Access)?;
226    let mut references = access
227        .query(signature, |_, _, work| Ok(work.vector()))
228        .map_err(Error::Access)?;
229    let refusal = |cause| Error::Resource { signature, cause };
230    for index in 0..unsigned.entries().len() {
231        admit(access, signature)?;
232        let entry = unsigned.entries()[index];
233        match entry.role {
234            unsigned::Role::SignatureTimestamp
235            | unsigned::Role::Archive
236            | unsigned::Role::SignatureAndReferencesTimestamp
237            | unsigned::Role::ReferencesTimestamp => {
238                let stamp =
239                    timestamp::read(access, &bound, entry.node).map_err(Error::Timestamp)?;
240                timestamps.try_push(stamp).map_err(refusal)?;
241            }
242            unsigned::Role::Certificates
243            | unsigned::Role::Revocation
244            | unsigned::Role::AttributeCertificates
245            | unsigned::Role::AttributeRevocation => {
246                let values = material::read(access, entry.node).map_err(Error::Property)?;
247                material.try_push(values).map_err(refusal)?;
248            }
249            unsigned::Role::AnyValidation | unsigned::Role::TimestampValidation => {
250                let values =
251                    validation::read(access, &bound, entry.node).map_err(Error::Validation)?;
252                validation.try_push(values).map_err(refusal)?;
253            }
254            unsigned::Role::RenewedDigests => {
255                let values = renewed::read(access, &unsigned, index).map_err(Error::Property)?;
256                renewed.try_push(values).map_err(refusal)?;
257            }
258            unsigned::Role::CertificateReferences
259            | unsigned::Role::RevocationReferences
260            | unsigned::Role::AttributeCertificateReferences
261            | unsigned::Role::AttributeRevocationReferences => {
262                let values = validation::read_references(access, signature, entry)
263                    .map_err(Error::Property)?;
264                references.try_push(values).map_err(refusal)?;
265            }
266            _ => {} // Preserved in the complete inventory for subsequent execution.
267        }
268    }
269    signed_objects(access, &bound, &signed, &mut timestamps, &mut commitments)?;
270    Ok(Preparation {
271        requirements,
272        bound,
273        signed,
274        header,
275        certificates,
276        formats,
277        commitments: commitments.into_owned(),
278        roles,
279        policy,
280        policy_store,
281        unsigned,
282        timestamps: timestamps.into_owned(),
283        material: material.into_owned(),
284        validation: validation.into_owned(),
285        renewed: renewed.into_owned(),
286        references: references.into_owned(),
287    })
288}
289
290fn admit<X: Xml, W: Work>(access: &Access<'_, X, W>, signature: X::Id) -> PrepareResult<(), X> {
291    access
292        .query(signature, |_, _, work| Ok(work.charge_work(1)))
293        .map_err(Error::Access)?
294        .map_err(|cause| Error::Resource { signature, cause })
295}
296
297fn signed_objects<X: ReferenceXml, W: Work>(
298    access: &Access<'_, X, W>,
299    bound: &binding::Selected<X>,
300    signed: &properties::Reading<X>,
301    timestamps: &mut OwnedVec<timestamp::Timestamp<X>, W>,
302    commitments: &mut OwnedVec<commitment::Prepared<X>, W>,
303) -> PrepareResult<(), X> {
304    let Some(objects) = signed.data_objects() else {
305        return Ok(());
306    };
307    for index in 0..objects.entries().len() {
308        admit(access, signed.signature())?;
309        let entry = objects.entries()[index];
310        if entry.role == properties::DataObjectRole::Commitment {
311            let value =
312                commitment::prepare(access, bound, signed, index).map_err(Error::Commitment)?;
313            commitments
314                .try_push(value)
315                .map_err(|cause| Error::Resource {
316                    signature: signed.signature(),
317                    cause,
318                })?;
319            continue;
320        }
321        let stamp = match entry.role {
322            properties::DataObjectRole::AllTimestamp => {
323                timestamp::read_all_data_objects(access, bound, signed, entry.node)
324            }
325            properties::DataObjectRole::IndividualTimestamp => {
326                timestamp::read_individual_data_objects(access, bound, signed, entry.node)
327            }
328            _ => continue,
329        }
330        .map_err(Error::Timestamp)?;
331        timestamps
332            .try_push(stamp)
333            .map_err(|cause| Error::Resource {
334                signature: signed.signature(),
335                cause,
336            })?;
337    }
338    Ok(())
339}