1use super::{CheckError, CheckTime, Checked, Context};
12use crate::{
13 Cades, Error, ISSUER,
14 prepare::{
15 Preparation,
16 encoding::{AttributeEncoding, SignerView},
17 },
18 preservation::Kind,
19 source::{DocumentId, Node},
20};
21use core::convert::Infallible;
22use structured_asn1::{
23 Oid, Reader,
24 memory::{Memory, OwnedMap, OwnedVec, Work},
25};
26use structured_cms::{
27 provider::SignatureProvider,
28 timestamp::{self as tsp, ComputedInput, ImprintContext, ImprintProof, InputFailure},
29};
30use structured_ltv::{
31 material::Coverage, pki::CheckedPath, source::Source as _, timestamp::Purpose,
32};
33use structured_pkix::algorithm::digest::AsnDigest;
34
35const FIELD: &str = "cades.references";
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Component {
40 SignatureTimestamp,
42 CertificateReferences,
44 RevocationReferences,
46}
47impl Component {
48 fn oid(self) -> Oid<'static> {
49 let bytes: &'static [u8] = match self {
50 Self::SignatureTimestamp => &[42, 134, 72, 134, 247, 13, 1, 9, 16, 2, 14],
51 Self::CertificateReferences => &[42, 134, 72, 134, 247, 13, 1, 9, 16, 2, 21],
52 Self::RevocationReferences => &[42, 134, 72, 134, 247, 13, 1, 9, 16, 2, 22],
53 };
54 Oid::from_bytes(bytes)
55 }
56}
57
58#[derive(Debug)]
60pub enum SelectionError {
61 Processing(Error),
63 WrongOccurrence,
65 Missing(Component),
67 Duplicate {
69 property: Node,
71 },
72 Cardinality {
74 property: Node,
76 },
77}
78impl From<Error> for SelectionError {
79 fn from(cause: Error) -> Self {
80 Self::Processing(cause)
81 }
82}
83
84pub struct ReferenceInput<'input, 'model, 'document> {
87 prepared: &'input Preparation<'model, 'document>,
88 operation: &'input Reader<'document>,
89 ordinal: usize,
90 parts: OwnedVec<&'document [u8]>,
91 covered: Option<OwnedMap<Node, bool>>,
92}
93impl<'s, 'm, 'd> ReferenceInput<'s, 'm, 'd> {
94 pub fn new(
98 prepared: &'s Preparation<'m, 'd>,
99 ordinal: usize,
100 operation: &'s Reader<'d>,
101 ) -> Result<Self, SelectionError> {
102 prepared
103 .document()
104 .message()
105 .require_session(operation, FIELD)
106 .map_err(Error::Cms)?;
107 let work = operation.execution();
108 let refusal = |cause| {
109 SelectionError::Processing(Error::resource(
110 cause,
111 FIELD,
112 prepared.signer().span().start,
113 ))
114 };
115 work.charge_work(1).map_err(refusal)?;
116 let target = prepared
117 .timestamps()
118 .get(ordinal)
119 .ok_or(SelectionError::WrongOccurrence)?;
120 let kind = target
121 .preservation_kind()
122 .ok_or(SelectionError::WrongOccurrence)?;
123 if !kind.is_reference_timestamp() || target.purpose() != Purpose::ValidationData {
124 return Err(SelectionError::WrongOccurrence);
125 }
126 let value = target.value().ok_or(SelectionError::WrongOccurrence)?;
127 let view = prepared.signer_encoding(target.owner(), operation)?;
128 let mut placed = false;
129 for index in 0..view.attributes() {
130 work.charge_work(1).map_err(refusal)?;
131 let attribute = view
132 .attribute(index)
133 .ok_or(SelectionError::WrongOccurrence)?;
134 if Node::of(attribute.span) == target.property() {
135 if attribute.single != Some(value.span()) {
136 return Err(SelectionError::Cardinality {
137 property: target.property(),
138 });
139 }
140 placed = true;
141 break;
142 }
143 }
144 if !placed {
145 return Err(SelectionError::WrongOccurrence);
146 }
147 let plan = Plan::read(view, kind, target.property(), &work)?;
148 let mut parts = work.vector();
149 let mut covered = work.map();
150 if kind == Kind::SignatureReferences {
151 for piece in prepared.signature_of(target.owner(), operation)? {
152 parts.try_push(piece.as_bytes()).map_err(refusal)?;
153 }
154 }
155 plan.emit(&work, &mut parts, &mut covered)?;
156 Ok(Self {
157 prepared,
158 operation,
159 ordinal,
160 parts: parts.into_owned(),
161 covered: Some(covered.into_owned()),
162 })
163 }
164}
165
166struct Plan<'v, 'd> {
170 view: SignerView<'v, 'd>,
171 signatures: bool,
172 certificate: AttributeEncoding<'d>,
173 revocation: AttributeEncoding<'d>,
174 at: Node,
175}
176impl<'v, 'd> Plan<'v, 'd> {
177 fn read(
178 view: SignerView<'v, 'd>,
179 kind: Kind,
180 at: Node,
181 work: &impl Work,
182 ) -> Result<Self, SelectionError> {
183 let mut certificate = None;
184 let mut revocation = None;
185 let signatures = kind == Kind::SignatureReferences;
186 let mut signature_found = false;
187 for index in 0..view.attributes() {
188 work.charge_work(12)
189 .map_err(|cause| Error::resource(cause, FIELD, at.span().start))?;
190 let attribute = view
191 .attribute(index)
192 .ok_or(SelectionError::WrongOccurrence)?;
193 let oid = attribute.kind.as_bytes();
194 if oid.len() != 11 {
195 continue;
196 }
197 if oid[..10] != [42, 134, 72, 134, 247, 13, 1, 9, 16, 2] {
198 continue;
199 }
200 match oid[10] {
201 14 if signatures => {
202 value(attribute)?;
203 signature_found = true;
204 }
205 21 => remember(&mut certificate, attribute)?,
206 22 => remember(&mut revocation, attribute)?,
207 _ => {}
208 }
209 }
210 if signatures && !signature_found {
211 return Err(SelectionError::Missing(Component::SignatureTimestamp));
212 }
213 Ok(Self {
214 view,
215 signatures,
216 at,
217 certificate: certificate
218 .ok_or(SelectionError::Missing(Component::CertificateReferences))?,
219 revocation: revocation
220 .ok_or(SelectionError::Missing(Component::RevocationReferences))?,
221 })
222 }
223 fn emit<W: Work>(
224 &self,
225 work: &W,
226 parts: &mut OwnedVec<&'d [u8], W>,
227 covered: &mut OwnedMap<Node, bool, W>,
228 ) -> Result<(), SelectionError> {
229 if self.signatures {
230 for index in 0..self.view.attributes() {
234 work.charge_work(12)
235 .map_err(|cause| Error::resource(cause, FIELD, self.at.span().start))?;
236 let attribute = self
237 .view
238 .attribute(index)
239 .ok_or(SelectionError::WrongOccurrence)?;
240 if attribute.kind == Component::SignatureTimestamp.oid() {
241 include(attribute, parts, covered)?;
242 }
243 }
244 }
245 include(self.certificate, parts, covered)?;
246 include(self.revocation, parts, covered)
247 }
248}
249fn value(attribute: AttributeEncoding<'_>) -> Result<structured_asn1::Span, SelectionError> {
250 attribute.single.ok_or(SelectionError::Cardinality {
251 property: Node::of(attribute.span),
252 })
253}
254fn remember<'d>(
255 slot: &mut Option<AttributeEncoding<'d>>,
256 attribute: AttributeEncoding<'d>,
257) -> Result<(), SelectionError> {
258 if slot.is_some() {
259 return Err(SelectionError::Duplicate {
260 property: Node::of(attribute.span),
261 });
262 }
263 value(attribute)?;
264 *slot = Some(attribute);
265 Ok(())
266}
267fn include<'d, W: Work>(
268 attribute: AttributeEncoding<'d>,
269 parts: &mut OwnedVec<&'d [u8], W>,
270 covered: &mut OwnedMap<Node, bool, W>,
271) -> Result<(), SelectionError> {
272 let value = value(attribute)?;
273 let refusal = |cause| Error::resource(cause, FIELD, attribute.span.start);
274 parts.try_push(attribute.contents).map_err(refusal)?;
275 covered
277 .try_insert(Node::of(attribute.span), false)
278 .map_err(refusal)?;
279 covered.try_insert(Node::of(value), true).map_err(refusal)?;
280 Ok(())
281}
282
283impl<P, PE> ComputedInput<P, PE> for ReferenceInput<'_, '_, '_> {
284 type Error = Infallible;
285 fn compute<'context, 'reader, 'document>(
286 &mut self,
287 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
288 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
289 where
290 P: AsnDigest<'document, Error = PE>,
291 {
292 let mut state = context.start()?;
293 for bytes in &self.parts {
294 state.write_from(bytes, self.operation)?;
297 }
298 let digest = state.finish()?;
299 Ok(context.compare(&digest)?)
300 }
301}
302
303pub struct ReferenceChecked<'model, 'document, 'path> {
306 timestamp: Checked<'model, 'document, 'path>,
307 coverage: Coverage<Node, DocumentId<'model, 'document>, (), Memory, Cades>,
308}
309impl<'m, 'd> ReferenceChecked<'m, 'd, '_> {
310 pub fn timestamp(&self) -> &Checked<'m, 'd, '_> {
312 &self.timestamp
313 }
314 pub fn coverage(&self) -> &Coverage<Node, DocumentId<'m, 'd>, (), Memory, Cades> {
316 &self.coverage
317 }
318}
319
320impl<'m, 'd> Context<'_, 'm, 'd> {
321 #[expect(
325 clippy::result_large_err,
326 reason = "original profile causes remain unboxed"
327 )]
328 pub fn check_references<'a, 'p, P, PE, RE>(
329 &'a self,
330 provider: &'a P,
331 path: &'p CheckedPath<'d>,
332 certificate_parent: &Reader<'d>,
333 time: &CheckTime<'_, 'd>,
334 input: &mut ReferenceInput<'_, 'm, 'd>,
335 recognizes: impl FnMut(
336 &tsp::TokenExtension<'_>,
337 &structured_asn1::memory::Execution<'_>,
338 ) -> Result<bool, RE>,
339 ) -> Result<ReferenceChecked<'m, 'd, 'p>, CheckError<Infallible, PE, RE>>
340 where
341 P: for<'scope> SignatureProvider<'scope, Error = PE>,
342 {
343 if self.ordinal != input.ordinal
344 || !core::ptr::eq(self.prepared, input.prepared)
345 || input.covered.is_none()
346 {
347 return Err(CheckError::Selection(Error::Foreign {
348 at: self.entry.property().span().start,
349 }));
350 }
351 let value = self
352 .entry
353 .value()
354 .ok_or(CheckError::Selection(Error::Foreign {
355 at: self.entry.property().span().start,
356 }))?;
357 let timestamp = self.check_computed_profile(
358 provider,
359 path,
360 certificate_parent,
361 time,
362 input,
363 (
364 recognizes,
365 |_| Ok::<(), tsp::ProfileFailure<Infallible>>(()),
366 ),
367 )?;
368 let coverage = Coverage::for_token(
369 ISSUER,
370 self.prepared.document().snapshot(),
371 self.entry.owner(),
372 self.entry.property(),
373 Node::of(value.span()),
374 Purpose::ValidationData,
375 input
376 .covered
377 .take()
378 .ok_or(CheckError::Selection(Error::Foreign {
379 at: self.entry.property().span().start,
380 }))?,
381 );
382 Ok(ReferenceChecked {
383 timestamp,
384 coverage,
385 })
386 }
387}