1use super::{ReferenceSearch, ref_catalogue::Memo};
4use crate::{
5 Xades,
6 binary::{self, BinaryXml, Location},
7 certificate::{Agreement, DigestProvider},
8 digest,
9 material::{
10 ExclusiveFindingKind,
11 exclusivity::{self, CaError, CaFact, ReferenceCa},
12 },
13 prepare::{Preparation, Prepared},
14 unsigned,
15 xml::{Access, Xml},
16};
17use core::ops::Range;
18use structured_asn1::digest::Digest;
19use structured_asn1::memory::{MemoryError, OwnedVec, Work};
20use structured_ltv::material::ArtifactUses;
21use structured_ltv::timestamp::TimestampScope;
22use structured_pkix::Certificate;
23
24pub struct CertificateRoleRow<N> {
26 property: usize,
27 entry: usize,
28 node: N,
29 search: ReferenceSearch,
30 range: Range<usize>,
31}
32impl<N: Copy> CertificateRoleRow<N> {
33 #[must_use]
35 pub fn property(&self) -> usize {
36 self.property
37 }
38 #[must_use]
40 pub fn entry(&self) -> usize {
41 self.entry
42 }
43 #[must_use]
45 pub fn node(&self) -> N {
46 self.node
47 }
48 #[must_use]
50 pub fn search(&self) -> ReferenceSearch {
51 self.search
52 }
53}
54
55pub struct CertificateRoleComparison<'certificate, 'document> {
57 certificate: &'certificate Certificate<'document>,
58 kind: ExclusiveFindingKind,
59 agreement: Agreement,
60}
61impl<'certificate, 'document> CertificateRoleComparison<'certificate, 'document> {
62 #[must_use]
64 pub fn certificate(&self) -> &'certificate Certificate<'document> {
65 self.certificate
66 }
67 #[must_use]
69 pub fn kind(&self) -> ExclusiveFindingKind {
70 self.kind
71 }
72 #[must_use]
75 pub fn agreement(&self) -> Agreement {
76 self.agreement
77 }
78}
79
80pub struct CertificateRoles<'prepared, 'certificate, 'document, N, D, G> {
84 prepared: &'prepared Preparation<N, D, G>,
85 rows: OwnedVec<CertificateRoleRow<N>>,
86 comparisons: OwnedVec<CertificateRoleComparison<'certificate, 'document>>,
87}
88impl<'p, 'c, 'd, N: Copy, D, G> CertificateRoles<'p, 'c, 'd, N, D, G> {
89 #[must_use]
91 pub fn prepared(&self) -> &'p Preparation<N, D, G> {
92 self.prepared
93 }
94 #[must_use]
96 pub fn rows(&self) -> &[CertificateRoleRow<N>] {
97 &self.rows
98 }
99 #[must_use]
101 pub fn comparisons(&self, row: usize) -> Option<&[CertificateRoleComparison<'c, 'd>]> {
102 self.comparisons.get(self.rows.get(row)?.range.clone())
103 }
104}
105
106pub enum CertificateRoleError<'certificate, 'document, PE> {
108 Scope,
110 Resource(MemoryError),
112 Certificate {
114 certificate: &'certificate Certificate<'document>,
116 cause: structured_pkix::Error,
118 },
119 Digest(digest::Error<structured_cms::stream::RangesError, PE>),
121}
122impl<PE: core::fmt::Debug> core::fmt::Debug for CertificateRoleError<'_, '_, PE> {
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 match self {
125 Self::Scope => f.write_str("Scope"),
126 Self::Resource(cause) => f.debug_tuple("Resource").field(cause).finish(),
127 Self::Certificate { cause, .. } => f.debug_tuple("Certificate").field(cause).finish(),
128 Self::Digest(cause) => f.debug_tuple("Digest").field(cause).finish(),
129 }
130 }
131}
132
133pub type CertificateRoleResult<'p, 'c, 'd, X, PE> = binary::BinaryResult<
135 CertificateRoles<'p, 'c, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
136 X,
137 CertificateRoleError<'c, 'd, PE>,
138>;
139
140fn compare_role_candidates<'c, 'd, 'q, X: Xml, P: Digest<'q>, W: Work>(
141 memo: &mut Memo<'q, 'd, P, W>,
142 candidates: &mut Option<OwnedVec<ReferenceCa<'c, 'd>, W>>,
143 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
144 comparisons: &mut OwnedVec<CertificateRoleComparison<'c, 'd>, W>,
145 identity: crate::certificate::FormatDigest<P::Identity>,
146 expected: &[u8],
147 work: &W,
148) -> Result<(), digest::Error<CertificateRoleError<'c, 'd, P::Error>, P::Error>> {
149 let method = memo
150 .method(identity, work)
151 .map_err(|cause| digest::Error::Source(CertificateRoleError::Digest(cause)))?;
152 if candidates.is_none() {
153 *candidates = Some(
154 exclusivity::reference_certificate_candidates(artifacts, work).map_err(|cause| {
155 digest::Error::Source(match cause {
156 CaError::Resource(cause) => CertificateRoleError::Resource(cause),
157 CaError::Certificate(certificate, cause) => {
158 CertificateRoleError::Certificate { certificate, cause }
159 }
160 })
161 })?,
162 );
163 }
164 for (ordinal, candidate) in candidates
165 .as_ref()
166 .expect("initialized candidates")
167 .iter()
168 .enumerate()
169 {
170 work.charge_work(1).map_err(digest::Error::Resource)?;
171 let agreement = memo
172 .compare(
173 ordinal,
174 candidate.certificate.as_bytes(),
175 method,
176 expected,
177 work,
178 )
179 .map_err(|cause| digest::Error::Source(CertificateRoleError::Digest(cause)))?;
180 let kind = match candidate.fact {
181 CaFact::Asserted => ExclusiveFindingKind::ForbiddenCa,
182 CaFact::Unstated => ExclusiveFindingKind::UnstatedCa,
183 CaFact::NotAsserted => unreachable!("non-CA is not an exclusion candidate"),
184 };
185 comparisons
186 .try_push(CertificateRoleComparison {
187 certificate: candidate.certificate,
188 kind,
189 agreement,
190 })
191 .map_err(digest::Error::Resource)?;
192 }
193 Ok(())
194}
195
196pub(super) fn role_error<'c, 'd, PE>(
197 cause: digest::Error<CertificateRoleError<'c, 'd, PE>, PE>,
198) -> CertificateRoleError<'c, 'd, PE> {
199 match cause {
200 digest::Error::ForeignDigest => CertificateRoleError::Digest(digest::Error::ForeignDigest),
201 digest::Error::Unstreamable => CertificateRoleError::Digest(digest::Error::Unstreamable),
202 digest::Error::Source(cause) => cause,
203 digest::Error::Provider(cause) => {
204 CertificateRoleError::Digest(digest::Error::Provider(cause))
205 }
206 digest::Error::Resource(cause) => CertificateRoleError::Resource(cause),
207 digest::Error::Stream(cause) => CertificateRoleError::Digest(digest::Error::Stream(cause)),
208 digest::Error::OutputWidth { expected, actual } => {
209 CertificateRoleError::Digest(digest::Error::OutputWidth { expected, actual })
210 }
211 }
212}
213
214pub fn check_certificate_reference_roles<
226 'p,
227 'c,
228 'd,
229 'q,
230 X: BinaryXml,
231 P: DigestProvider<'q, X>,
232 W: Work,
233>(
234 xml: &X,
235 prepared: &'p Prepared<X>,
236 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
237 provider: &'q P,
238 work: &W,
239) -> CertificateRoleResult<'p, 'c, 'd, X, P::Error> {
240 let source = prepared.bound().containers();
241 let location = Location {
242 snapshot: source.snapshot(),
243 node: source.signature(),
244 };
245 let access = Access::new(xml, location.snapshot, work);
246 access
247 .query(location.node, |_, _, _| Ok(()))
248 .map_err(|cause| binary::Error::Access { location, cause })?;
249 let failed = |cause| binary::Error::Callback { location, cause };
250 let required = artifacts.inputs().requirements();
251 if required.scope().location().snapshot != location.snapshot
252 || !matches!(required.scope(), TimestampScope::All(_))
253 || !access
254 .equal_id(required.signature(), location.node)
255 .map_err(|cause| failed(CertificateRoleError::Resource(cause)))?
256 {
257 return Err(failed(CertificateRoleError::Scope));
258 }
259 let mut candidates: Option<OwnedVec<ReferenceCa<'c, 'd>, W>> = None;
260 let mut memo = Memo::<P, W>::new(provider, work);
261 let mut rows = work.vector();
262 let mut comparisons = work.vector();
263 for (property, refs) in prepared.validation_references().iter().enumerate() {
264 work.charge_work(1)
265 .map_err(|cause| failed(CertificateRoleError::Resource(cause)))?;
266 if refs.role() != unsigned::Role::CertificateReferences {
267 continue;
268 }
269 for (entry, reference) in refs.entries().iter().enumerate() {
270 work.charge_work(1)
271 .map_err(|cause| failed(CertificateRoleError::Resource(cause)))?;
272 let (method, value) = reference.digest().expect("mandatory CertDigest");
273 let start = comparisons.len();
274 let checked = digest::check_with(
275 xml,
276 digest::Reference {
277 snapshot: refs.snapshot(),
278 method,
279 value,
280 },
281 provider,
282 work,
283 |identity, expected, work| {
284 compare_role_candidates::<X, P, W>(
285 &mut memo,
286 &mut candidates,
287 artifacts,
288 &mut comparisons,
289 identity,
290 expected,
291 work,
292 )
293 },
294 )
295 .map_err(|cause| cause.map_callback(role_error))?;
296 let search = match checked {
297 digest::Checked::Value(()) => ReferenceSearch::Compared,
298 digest::Checked::Answer(answer) => ReferenceSearch::Unavailable(answer),
299 };
300 rows.try_push(CertificateRoleRow {
301 property,
302 entry,
303 node: reference.node(),
304 search,
305 range: start..comparisons.len(),
306 })
307 .map_err(|cause| failed(CertificateRoleError::Resource(cause)))?;
308 }
309 }
310 access
311 .query(location.node, |_, _, _| Ok(()))
312 .map_err(|cause| binary::Error::Access { location, cause })?;
313 Ok(CertificateRoles {
314 prepared,
315 rows: rows.into_owned(),
316 comparisons: comparisons.into_owned(),
317 })
318}