1use super::{ReferenceDigestError, ReferenceSearch, ref_catalogue::Memo};
4use crate::{
5 binary::{self, BinaryXml, Location},
6 certificate::{Agreement, CheckedPathCertificate, DigestProvider},
7 digest,
8 prepare::{Preparation, Prepared},
9 unsigned,
10 xml::{Access, Xml},
11};
12use core::ops::Range;
13use structured_asn1::digest::Digest;
14use structured_asn1::memory::{MemoryError, OwnedVec, Work};
15use structured_ltv::material::PathCertificate;
16use structured_ltv::pki::CheckedPath;
17
18pub struct RootReferenceRow<N> {
20 property: usize,
21 entry: usize,
22 node: N,
23 search: ReferenceSearch,
24 range: Range<usize>,
25}
26impl<N: Copy> RootReferenceRow<N> {
27 #[must_use]
29 pub fn property(&self) -> usize {
30 self.property
31 }
32 #[must_use]
34 pub fn entry(&self) -> usize {
35 self.entry
36 }
37 #[must_use]
39 pub fn node(&self) -> N {
40 self.node
41 }
42 #[must_use]
44 pub fn search(&self) -> ReferenceSearch {
45 self.search
46 }
47}
48
49pub struct RootPathReferences<'prepared, 'path, 'document, N, D, G> {
53 prepared: &'prepared Preparation<N, D, G>,
54 path: &'path CheckedPath<'document>,
55 rows: OwnedVec<RootReferenceRow<N>>,
56 comparisons: OwnedVec<Agreement>,
57 root_rules: bool,
58}
59impl<'p, 'v, 'd, N: Copy, D: Copy, G: Copy> RootPathReferences<'p, 'v, 'd, N, D, G> {
60 #[must_use]
62 pub fn prepared(&self) -> &'p Preparation<N, D, G> {
63 self.prepared
64 }
65 #[must_use]
67 pub fn path(&self) -> &'v CheckedPath<'d> {
68 self.path
69 }
70 #[must_use]
72 pub fn rows(&self) -> &[RootReferenceRow<N>] {
73 &self.rows
74 }
75 #[must_use]
78 pub fn comparisons(&self, row: usize) -> Option<&[Agreement]> {
79 let row = self.rows.get(row)?;
80 self.comparisons.get(row.range.clone())
81 }
82 #[must_use]
84 pub fn candidate(
85 &self,
86 index: usize,
87 ) -> Option<(PathCertificate, &'v structured_pkix::Certificate<'d>)> {
88 candidate(self.path, index)
89 }
90 #[must_use]
94 pub fn root_path_rules_met(&self) -> bool {
95 self.root_rules
96 }
97}
98
99pub type RootPathReferenceResult<'p, 'v, 'd, X, E> = binary::BinaryResult<
101 RootPathReferences<'p, 'v, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
102 X,
103 ReferenceDigestError<E>,
104>;
105
106fn candidate<'v, 'd>(
107 path: &'v CheckedPath<'d>,
108 mut ordinal: usize,
109) -> Option<(PathCertificate, &'v structured_pkix::Certificate<'d>)> {
110 if let Some(anchor) = path.anchor().certificate() {
111 if ordinal == 0 {
112 return Some((PathCertificate::Anchor, anchor));
113 }
114 ordinal -= 1;
115 }
116 path.members()
117 .get(ordinal)
118 .map(|member| (PathCertificate::Member(ordinal), member.certificate()))
119}
120
121fn candidate_ids<W: Work>(
122 path: &CheckedPath<'_>,
123 work: &W,
124) -> Result<OwnedVec<usize, W>, MemoryError> {
125 let mut ids = work.vector();
126 let mut index = work.map();
127 let mut ordinal = 0;
128 while let Some((_, certificate)) = candidate(path, ordinal) {
129 work.charge_work(1)?;
130 let bytes = certificate.as_bytes();
131 let identity = if let Some(&identity) = index.get(&bytes, work)? {
132 identity
133 } else {
134 index.try_insert(bytes, ordinal)?;
135 ordinal
136 };
137 ids.try_push(identity)?;
138 ordinal += 1;
139 }
140 Ok(ids)
141}
142
143struct PathComparisons<'provider, 'path, 'document, P: Digest<'provider>, W: Work> {
144 path: &'path CheckedPath<'document>,
145 memo: Memo<'provider, 'document, P, W>,
146 ids: Option<OwnedVec<usize, W>>,
147 covered: OwnedVec<bool, W>,
148 comparisons: OwnedVec<Agreement, W>,
149}
150impl<'provider, 'path, 'document, P: Digest<'provider>, W: Work>
151 PathComparisons<'provider, 'path, 'document, P, W>
152{
153 fn new(path: &'path CheckedPath<'document>, provider: &'provider P, work: &W) -> Self {
154 Self {
155 path,
156 memo: Memo::new(provider, work),
157 ids: None,
158 covered: work.vector(),
159 comparisons: work.vector(),
160 }
161 }
162 fn compare(
163 &mut self,
164 identity: crate::certificate::FormatDigest<P::Identity>,
165 expected: &[u8],
166 work: &W,
167 ) -> Result<(), digest::Error<structured_cms::stream::RangesError, P::Error>> {
168 let method = self.memo.method(identity, work)?;
169 if self.ids.is_none() {
170 let ids = candidate_ids(self.path, work).map_err(digest::Error::Resource)?;
171 let mut covered = work.vector();
172 covered
173 .try_reserve_exact(ids.len())
174 .map_err(digest::Error::Resource)?;
175 for _ in 0..ids.len() {
176 covered.try_push(false).map_err(digest::Error::Resource)?;
177 }
178 self.covered = covered;
179 self.ids = Some(ids);
180 }
181 let ids = self
182 .ids
183 .as_ref()
184 .expect("admitted original path candidates");
185 for ordinal in 0..ids.len() {
186 work.charge_work(1).map_err(digest::Error::Resource)?;
187 let (_, certificate) =
188 candidate(self.path, ordinal).expect("original immutable path candidate");
189 let agreement =
190 self.memo
191 .compare(ids[ordinal], certificate.as_bytes(), method, expected, work)?;
192 if agreement == Agreement::Matches {
193 self.covered[ordinal] = true;
194 }
195 self.comparisons
196 .try_push(agreement)
197 .map_err(digest::Error::Resource)?;
198 }
199 Ok(())
200 }
201}
202
203pub fn check_root_certificate_references<
216 'p,
217 'v,
218 'd,
219 'q,
220 X: BinaryXml,
221 P: DigestProvider<'q, X>,
222 W: Work,
223>(
224 xml: &X,
225 prepared: &'p Prepared<X>,
226 selected: &CheckedPathCertificate<'v, 'd, X::Id, X::Document, X::Generation>,
227 provider: &'q P,
228 work: &W,
229) -> RootPathReferenceResult<'p, 'v, 'd, X, P::Error> {
230 let source = prepared.bound().containers();
231 let location = Location {
232 snapshot: source.snapshot(),
233 node: source.signature(),
234 };
235 let access = Access::new(xml, location.snapshot, work);
236 access
237 .query(location.node, |_, _, _| Ok(()))
238 .map_err(|cause| binary::Error::Access { location, cause })?;
239 if !core::ptr::eq(selected.selected().reference(), prepared.certificates()) {
240 return Err(binary::Error::Callback {
241 location,
242 cause: ReferenceDigestError::Foreign,
243 });
244 }
245 if !selected.is_bound() {
246 return Err(binary::Error::Callback {
247 location,
248 cause: ReferenceDigestError::UnboundPath,
249 });
250 }
251 let resource = |cause| binary::Error::Callback {
252 location,
253 cause: ReferenceDigestError::Digest(digest::Error::Resource(cause)),
254 };
255 let path = selected.digests().path();
256 let mut state = PathComparisons::<P, W>::new(path, provider, work);
257 let mut rows = work.vector();
258 let mut root_rules = true;
259 for (property, references) in prepared.validation_references().iter().enumerate() {
260 work.charge_work(1).map_err(resource)?;
261 if references.role() != unsigned::Role::CertificateReferences {
262 continue;
263 }
264 work.charge_work(
265 u64::try_from(state.covered.len()).map_err(|_| resource(MemoryError::WorkExceeded))?,
266 )
267 .map_err(resource)?;
268 state.covered.fill(false);
269 let mut signer_excluded = true;
270 for (entry, reference) in references.entries().iter().enumerate() {
271 work.charge_work(1).map_err(resource)?;
272 let (method, value) = reference.digest().expect("original mandatory CertDigest");
273 let start = state.comparisons.len();
274 let checked = digest::check_with(
275 xml,
276 digest::Reference {
277 snapshot: references.snapshot(),
278 method,
279 value,
280 },
281 provider,
282 work,
283 |identity, expected, work| state.compare(identity, expected, work),
284 )
285 .map_err(|cause| cause.map_callback(ReferenceDigestError::Digest))?;
286 let search = match checked {
287 digest::Checked::Value(()) => {
288 signer_excluded &= state.comparisons.last() == Some(&Agreement::Differs);
289 ReferenceSearch::Compared
290 }
291 digest::Checked::Answer(answer) => {
292 signer_excluded = false;
293 ReferenceSearch::Unavailable(answer)
294 }
295 };
296 rows.try_push(RootReferenceRow {
297 property,
298 entry,
299 node: reference.node(),
300 search,
301 range: start..state.comparisons.len(),
302 })
303 .map_err(resource)?;
304 }
305 if state.covered.is_empty() {
306 root_rules = false;
307 continue;
308 }
309 let signer = state.covered.len() - 1;
310 for ordinal in 0..signer {
311 work.charge_work(1).map_err(resource)?;
312 root_rules &= state.covered[ordinal];
313 }
314 root_rules &= signer_excluded;
315 }
316 access
317 .query(location.node, |_, _, _| Ok(()))
318 .map_err(|cause| binary::Error::Access { location, cause })?;
319 Ok(RootPathReferences {
320 prepared,
321 path,
322 rows: rows.into_owned(),
323 comparisons: state.comparisons.into_owned(),
324 root_rules,
325 })
326}