1use super::check::{self, Input, Mode};
4use crate::{
5 binding::{Bound, Status},
6 certificate::{self, Agreement, DigestProvider},
7 container::DS,
8 digest,
9 produce::{Emit, Error, Transaction},
10 reference::ObjectLocation,
11 timestamp::{TimestampXml, XADES_141},
12 xml::{Access, Snapshot},
13};
14use structured_asn1::{
15 digest::Resolution,
16 memory::{OwnedVec, Work},
17};
18
19#[derive(Debug, Clone, Copy)]
22pub struct Candidate<N> {
23 pub reference: N,
25 pub location: ObjectLocation,
27 pub status: Status,
29}
30
31#[derive(Debug)]
33pub enum CreationError<E, PE, N, D, G> {
34 Input(crate::timestamp::InputError<E, N>),
36 Processing(super::CheckError<E, PE, N, D, G>),
38 Algorithm {
40 method: N,
42 answer: Agreement,
44 },
45 NewAlgorithm(Agreement),
47 NewProvider(PE),
49 Ineligible {
51 reference: N,
53 },
54 Unchanged {
56 reference: N,
58 },
59}
60
61struct Pair<N, O> {
62 reference: N,
63 instruction: O,
64 object: O,
65}
66
67pub struct Renewal<'method, N, D, G, O> {
71 snapshot: Snapshot<D, G>,
72 signature: N,
73 parent: Option<N>,
74 canonicalization: N,
75 digest: &'method str,
76 pairs: OwnedVec<Pair<N, O>>,
77}
78
79impl<N: Copy, D: Copy + Eq, G: Copy + Eq, O: AsRef<[u8]>> Renewal<'_, N, D, G, O> {
80 #[must_use]
82 pub fn len(&self) -> usize {
83 self.pairs.len()
84 }
85 #[must_use]
87 pub fn is_empty(&self) -> bool {
88 self.pairs.is_empty()
89 }
90
91 pub fn write<B: Transaction<Id = N, Document = D, Generation = G>, W: Work>(
98 &self,
99 writer: &mut B,
100 work: &W,
101 ) -> Result<(), Error<B::Error>>
102 where
103 N: structured_asn1::memory::HashWork,
104 {
105 work.charge_work(1).map_err(Error::Resource)?;
106 if writer.base() != self.snapshot
107 || !structured_ltv::source::equal_id(writer.signature(), self.signature, work)
108 .map_err(Error::Resource)?
109 || !crate::produce::same_parent(writer.unsigned_parent(), self.parent, work)
110 .map_err(Error::Resource)?
111 {
112 return Err(Error::Foreign);
113 }
114 if self.pairs.is_empty() {
115 return Ok(());
116 }
117 let mut emit = Emit { writer, work };
118 emit.begin(XADES_141, "RenewedDigestsV2")?;
119 work.charge_work(1).map_err(Error::Resource)?;
120 emit.writer
121 .method(self.canonicalization, work)
122 .map_err(Error::Writer)?;
123 emit.begin(DS, "DigestMethod")?;
124 emit.attribute("Algorithm", self.digest)?;
125 emit.end()?;
126 for pair in &self.pairs {
127 emit.begin(XADES_141, "RecomputedDigestValue")?;
128 emit.binary(XADES_141, "NewSDODigestValue", pair.object.as_ref())?;
129 emit.binary(XADES_141, "OriginalRefDigest", pair.instruction.as_ref())?;
130 emit.end()?;
131 }
132 emit.end()?;
133 work.charge_work(1).map_err(Error::Resource)?;
134 if emit.writer.base() != self.snapshot
135 || !structured_ltv::source::equal_id(emit.writer.signature(), self.signature, work)
136 .map_err(Error::Resource)?
137 || !crate::produce::same_parent(emit.writer.unsigned_parent(), self.parent, work)
138 .map_err(Error::Resource)?
139 {
140 return Err(Error::Foreign);
141 }
142 Ok(())
143 }
144 pub fn references(&self) -> impl ExactSizeIterator<Item = N> + '_ {
146 self.pairs.iter().map(|p| p.reference)
147 }
148}
149
150pub type CreationResult<T, X, PE> = Result<
152 T,
153 CreationError<
154 <X as crate::xml::Xml>::Error,
155 PE,
156 <X as crate::xml::Xml>::Id,
157 <X as crate::xml::Xml>::Document,
158 <X as crate::xml::Xml>::Generation,
159 >,
160>;
161pub type Created<'method, 'p, X, P> = Renewal<
163 'method,
164 <X as crate::xml::Xml>::Id,
165 <X as crate::xml::Xml>::Document,
166 <X as crate::xml::Xml>::Generation,
167 <P as structured_asn1::digest::Digest<'p>>::Output,
168>;
169
170pub trait MethodProvider<'p, X: crate::xml::Xml>: DigestProvider<'p, X> {
173 fn resolve_new<W: Work>(
181 &self,
182 algorithm: &str,
183 work: &W,
184 ) -> Result<Resolution<Self::Identity>, Self::Error>;
185}
186
187pub fn create<'method, 'p, X, P, W>(
193 xml: &X,
194 bound: &Bound<X::Id, X::Document, X::Generation>,
195 canonicalization: X::Id,
196 new_method: &'method str,
197 provider: &'p P,
198 work: &W,
199 selected: impl FnMut(Candidate<X::Id>, &W) -> Result<bool, P::Error>,
200) -> CreationResult<Created<'method, 'p, X, P>, X, P::Error>
201where
202 X: TimestampXml,
203 P: MethodProvider<'p, X>,
204 W: Work,
205{
206 let layout = bound.containers();
207 let access = Access::new(xml, layout.snapshot(), work);
208 let processing = CreationError::Processing;
209 let resource = |cause| {
210 processing(super::CheckError::Resource {
211 node: layout.signature(),
212 cause,
213 })
214 };
215 let parent = match layout.unsigned() {
216 Some(unsigned) => crate::timestamp::optional(
217 &access,
218 unsigned,
219 crate::container::XADES,
220 "UnsignedSignatureProperties",
221 )
222 .map_err(CreationError::Input)?,
223 None => None,
224 };
225 access
226 .query(layout.signature(), |_, _, _| Ok(()))
227 .map_err(|e| processing(super::CheckError::Access(e)))?;
228 if !access
229 .has_name(
230 canonicalization,
231 crate::xml::Name {
232 namespace: crate::container::DS,
233 local: "CanonicalizationMethod",
234 },
235 )
236 .map_err(|e| processing(super::CheckError::Access(e)))?
237 {
238 return Err(CreationError::Ineligible {
239 reference: canonicalization,
240 });
241 }
242 let identity = resolve_new(&access, layout.signature(), new_method, provider)?;
243 let choices = choose(&access, layout.signature(), &identity, provider, selected)?;
244 let mut pairs = work.vector();
245 pairs.try_reserve_exact(choices.len()).map_err(resource)?;
246 for reference in choices {
247 let mut source = Input {
248 xml,
249 snapshot: layout.snapshot(),
250 mode: Mode::Canonical {
251 node: reference,
252 method: canonicalization,
253 },
254 };
255 let instruction = computed(&identity, &mut source, provider, work, reference)?;
256 source.mode = Mode::Reference {
257 signature: layout.signature(),
258 node: reference,
259 };
260 let object = computed(&identity, &mut source, provider, work, reference)?;
261 pairs
262 .try_push(Pair {
263 reference,
264 instruction,
265 object,
266 })
267 .map_err(resource)?;
268 }
269 access
270 .query(layout.signature(), |_, _, _| Ok(()))
271 .map_err(|e| processing(super::CheckError::Access(e)))?;
272 Ok(Renewal {
273 snapshot: layout.snapshot(),
274 signature: layout.signature(),
275 parent,
276 canonicalization,
277 digest: new_method,
278 pairs: pairs.into_owned(),
279 })
280}
281
282fn resolve_new<'p, X: TimestampXml, P: MethodProvider<'p, X>, W: Work>(
283 access: &Access<'_, X, W>,
284 signature: X::Id,
285 algorithm: &str,
286 provider: &P,
287) -> CreationResult<certificate::FormatDigest<P::Identity>, X, P::Error> {
288 if let Some(answer) =
289 certificate::preflight_algorithm(algorithm, access.execution()).map_err(|cause| {
290 CreationError::Processing(super::CheckError::Resource {
291 node: signature,
292 cause,
293 })
294 })?
295 {
296 return Err(CreationError::NewAlgorithm(answer));
297 }
298 let resolved = access
299 .query(signature, |_, _, work| {
300 Ok(provider.resolve_new(algorithm, work))
301 })
302 .map_err(|e| CreationError::Processing(super::CheckError::Access(e)))?
303 .map_err(CreationError::NewProvider)?;
304 let resolved = match resolved {
305 Resolution::Supported(identity) => identity,
306 Resolution::Unsupported => return Err(CreationError::NewAlgorithm(Agreement::Unsupported)),
307 Resolution::Malformed => {
308 return Err(CreationError::NewAlgorithm(Agreement::MalformedMethod));
309 }
310 };
311 match certificate::baseline_digest::<X, P>(resolved, provider) {
312 certificate::Expected::Ready(identity) => Ok(identity),
313 certificate::Expected::Answer(answer) => Err(CreationError::NewAlgorithm(answer)),
314 }
315}
316
317fn choose<'p, X: TimestampXml, P: DigestProvider<'p, X>, W: Work>(
318 access: &Access<'_, X, W>,
319 signature: X::Id,
320 identity: &certificate::FormatDigest<P::Identity>,
321 provider: &P,
322 mut selected: impl FnMut(Candidate<X::Id>, &W) -> Result<bool, P::Error>,
323) -> CreationResult<OwnedVec<X::Id>, X, P::Error> {
324 let work = access.execution();
325 let processing = CreationError::Processing;
326 let resource = |cause| {
327 processing(super::CheckError::Resource {
328 node: signature,
329 cause,
330 })
331 };
332 let instructions = check::inventory(access, signature, signature).map_err(processing)?;
333 let mut choices = work.vector();
334 for instruction in instructions {
335 work.charge_work(1).map_err(resource)?;
336 let choose = selected(
337 Candidate {
338 reference: instruction.node,
339 location: instruction.location,
340 status: instruction.status,
341 },
342 work,
343 )
344 .map_err(|cause| {
345 processing(super::CheckError::Provider {
346 node: instruction.node,
347 cause,
348 })
349 })?;
350 if !choose {
351 continue;
352 }
353 if instruction.location != ObjectLocation::Detached || instruction.status != Status::Valid {
354 return Err(CreationError::Ineligible {
355 reference: instruction.node,
356 });
357 }
358 let method = check::original_method(access, instruction.node)
359 .map_err(|e| processing(super::CheckError::Property(e)))?;
360 let old = match check::resolve(access, method, provider).map_err(processing)? {
361 certificate::Expected::Ready(identity) => identity,
362 certificate::Expected::Answer(answer) => {
363 return Err(CreationError::Algorithm { method, answer });
364 }
365 };
366 work.charge_work(1).map_err(resource)?;
367 if provider
368 .compare(old.identity(), identity.identity(), work)
369 .map_err(|cause| {
370 processing(super::CheckError::Provider {
371 node: method,
372 cause,
373 })
374 })?
375 == core::cmp::Ordering::Equal
376 {
377 return Err(CreationError::Unchanged {
378 reference: instruction.node,
379 });
380 }
381 choices.try_push(instruction.node).map_err(resource)?;
382 }
383 Ok(choices.into_owned())
384}
385
386fn computed<'p, X: TimestampXml, P: DigestProvider<'p, X>, W: Work>(
387 identity: &certificate::FormatDigest<P::Identity>,
388 source: &mut Input<'_, X>,
389 provider: &'p P,
390 work: &W,
391 node: X::Id,
392) -> CreationResult<P::Output, X, P::Error> {
393 match digest::compute_resolved::<P, _, _>(identity, source, provider, work)
394 .map_err(|cause| CreationError::Processing(super::CheckError::Digest { node, cause }))?
395 {
396 Resolution::Supported(output) => Ok(output),
397 Resolution::Unsupported => Err(CreationError::Algorithm {
398 method: node,
399 answer: Agreement::Unsupported,
400 }),
401 Resolution::Malformed => Err(CreationError::Algorithm {
402 method: node,
403 answer: Agreement::MalformedMethod,
404 }),
405 }
406}