Skip to main content

structured_xades/
produce.rs

1//! `XAdES` construction over an XML owner's unpublished staging transaction.
2//!
3//! Construction is not a validation receipt. After signing or augmentation,
4//! run the existing preparation and baseline assessments over the resulting
5//! generation. This module does not serialize XML, sign, fetch evidence, read
6//! a clock or choose application trust. See [`Writer`] for the mutation boundary.
7
8use crate::{
9    container::{DS, XADES},
10    xml::Name,
11};
12use structured_asn1::memory::{MemoryError, Work};
13
14mod basic;
15mod material;
16mod timestamp;
17pub use basic::{Basic, BasicReferences, DataObject, ReferenceKind};
18pub use material::write_material;
19pub use material::{Material, MaterialKind, Materials};
20pub use timestamp::{Timestamp, TimestampKind};
21
22mod augmentation;
23pub use augmentation::{
24    Augmentation, Checked, CheckedFor, InputResult, PreparationError, Presence, ResponseCheck,
25    ResponseError, ResponseResult, Transaction,
26};
27
28/// XML-owned lexical validation requested by the shared property generator.
29/// The owner reuses its existing XML Schema 1.0 datatype kernels.
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Datatype {
32    /// XML character data; no extra schema facet.
33    String,
34    /// Unique XML ID/NCName, registered in the owner's ID domain.
35    Id,
36    /// Complete XML Schema anyURI with its whitespace processing.
37    AnyUri,
38    /// XML Schema dateTime with a required explicit timezone. This is a claimed
39    /// instant, not clock acquisition or authenticated temporal evidence.
40    ZonedDateTime,
41}
42/// Borrowed construction text and the datatype the owner must validate.
43#[derive(Debug, Clone, Copy)]
44pub struct Value<'a> {
45    /// Complete supplied lexical value, never markup.
46    pub lexical: &'a str,
47    /// Generic XML datatype selected by the shared property rules.
48    pub datatype: Datatype,
49}
50
51/// One balanced XML construction instruction. Names are expanded; the owner
52/// selects prefixes and escapes values without changing their semantics.
53#[derive(Debug, Clone, Copy)]
54pub enum Event<'a> {
55    /// Append a new element and enter it.
56    Begin(Name<'static>),
57    /// Set an attribute on the current new element, before its children.
58    Attribute(Name<'static>, Value<'a>),
59    /// Append character data, not markup.
60    Text(Value<'a>),
61    /// Append the base64Binary representation of these exact complete bytes.
62    /// No line wrapping, decoding, re-encoding of ASN.1, or payload substitution.
63    Binary(&'a [u8]),
64    /// Close the most recently begun element.
65    End,
66}
67
68/// XML-owned staging capability, not a second DOM or signing dispatcher.
69///
70/// The caller opens an unpublished transaction at the precise insertion point.
71/// Any failure discards its entire draft. Successful emission leaves the draft
72/// unpublished until original signed inputs, earlier archive inputs, namespace
73/// context and generation invariants are checked by that owner and existing
74/// shared validators. Augmentation may append unsigned properties, never edit
75/// previously protected subtrees or add an Object after an earlier archive.
76///
77/// Each callback checks XML syntax, datatype/ID rules, actual source/generation,
78/// and its compiled policy. It admits ALL scanning, base64/escaping, allocation
79/// capacity and tree growth through `work` before doing them. Attribute strings
80/// and binary slices cannot escape this call unless copied with retained charge.
81/// A callback's success is not cryptographic evidence or attained baseline level.
82pub trait Writer {
83    /// Original XML, policy, allocation or processing refusal.
84    type Error;
85    /// Execute this event in the current unpublished transaction.
86    fn write<W: Work>(&mut self, event: Event<'_>, work: &W) -> Result<(), Self::Error>;
87}
88
89/// Construction refused without publishing a partial draft.
90#[derive(Debug)]
91pub enum Error<E> {
92    /// Original XML-owner callback cause, unchanged.
93    Writer(E),
94    /// Original capacity or work refusal before processing.
95    Resource(MemoryError),
96    /// A required new value is absent or violates its format syntax.
97    Value(&'static str),
98    /// Source document, generation, selected signature or insertion point differs.
99    Foreign,
100    /// The existing shared stage report has unmet conditions or lacks LT data.
101    Prerequisites,
102}
103
104pub(crate) const fn name(namespace: &'static str, local: &'static str) -> Name<'static> {
105    Name { namespace, local }
106}
107pub(crate) const fn attr(local: &'static str) -> Name<'static> {
108    name("", local)
109}
110
111pub(crate) fn same_parent<N: Copy + Eq + structured_asn1::memory::HashWork, W: Work>(
112    left: Option<N>,
113    right: Option<N>,
114    work: &W,
115) -> Result<bool, MemoryError> {
116    match (left, right) {
117        (Some(left), Some(right)) => structured_ltv::source::equal_id(left, right, work),
118        (None, None) => Ok(true),
119        (Some(_), None) | (None, Some(_)) => Ok(false),
120    }
121}
122
123pub(crate) struct Emit<'a, B: Writer, W: Work> {
124    pub writer: &'a mut B,
125    pub work: &'a W,
126}
127impl<B: Writer, W: Work> Emit<'_, B, W> {
128    pub fn event(&mut self, event: Event<'_>) -> Result<(), Error<B::Error>> {
129        self.work.charge_work(1).map_err(Error::Resource)?;
130        self.writer.write(event, self.work).map_err(Error::Writer)
131    }
132    pub fn begin(
133        &mut self,
134        namespace: &'static str,
135        local: &'static str,
136    ) -> Result<(), Error<B::Error>> {
137        self.event(Event::Begin(name(namespace, local)))
138    }
139    pub fn end(&mut self) -> Result<(), Error<B::Error>> {
140        self.event(Event::End)
141    }
142    pub fn attribute(&mut self, local: &'static str, value: &str) -> Result<(), Error<B::Error>> {
143        let datatype = match local {
144            "Id" => Datatype::Id,
145            "Target" | "ObjectReference" | "Algorithm" => Datatype::AnyUri,
146            _ => Datatype::String,
147        };
148        self.event(Event::Attribute(
149            attr(local),
150            Value {
151                lexical: value,
152                datatype,
153            },
154        ))
155    }
156    pub fn text(
157        &mut self,
158        namespace: &'static str,
159        local: &'static str,
160        value: &str,
161    ) -> Result<(), Error<B::Error>> {
162        self.begin(namespace, local)?;
163        self.event(Event::Text(Value {
164            lexical: value,
165            datatype: Datatype::String,
166        }))?;
167        self.end()
168    }
169    pub fn binary(
170        &mut self,
171        namespace: &'static str,
172        local: &'static str,
173        value: &[u8],
174    ) -> Result<(), Error<B::Error>> {
175        self.begin(namespace, local)?;
176        self.event(Event::Binary(value))?;
177        self.end()
178    }
179    pub fn digest(&mut self, method: &str, value: &[u8]) -> Result<(), Error<B::Error>> {
180        self.begin(DS, "DigestMethod")?;
181        self.attribute("Algorithm", method)?;
182        self.end()?;
183        self.binary(DS, "DigestValue", value)
184    }
185}