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}