1use super::{
4 CertificateRoleError, ReferenceKind, ReferenceSearch,
5 ref_catalogue::{CandidatePools, Memo},
6 ref_envelope::basic_payload,
7 ref_roles::role_error,
8 ref_status_required::{
9 self, CredentialRequired, CredentialStatusSource, PathStatusSource, Required,
10 },
11};
12use crate::{
13 Xades,
14 binary::{self, BinaryXml, Location},
15 certificate::{Agreement, DigestProvider, FormatDigest},
16 digest,
17 material::{
18 self, ExclusiveFindingKind,
19 exclusivity::{self, CaError, CaFact, ReferenceStatus},
20 },
21 prepare::{Preparation, Prepared},
22 timestamp::CarriedMaterial,
23 unsigned,
24 xml::{Access, Xml},
25};
26use core::ops::Range;
27use structured_asn1::memory::{MemoryError, OwnedMap, OwnedVec, Work};
28use structured_ltv::material::{ArtifactRef, ArtifactSource, ArtifactUses};
29use structured_ltv::timestamp::TimestampScope;
30use structured_pkix::{Certificate, ocsp::Response};
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum StatusRoleAgreement {
35 Compared(Agreement),
37 EnvelopeUnprovided,
40}
41
42pub struct StatusSourceComparison {
44 source: usize,
45 agreement: StatusRoleAgreement,
46}
47impl StatusSourceComparison {
48 #[must_use]
50 pub fn source(&self) -> usize {
51 self.source
52 }
53 #[must_use]
55 pub fn agreement(&self) -> StatusRoleAgreement {
56 self.agreement
57 }
58}
59
60pub struct StatusRoleComparison<'certificate, 'document, N, D, G> {
62 artifact: ArtifactRef<'certificate, 'document>,
63 target: &'certificate Certificate<'document>,
64 kind: ExclusiveFindingKind,
65 agreement: StatusRoleAgreement,
66 envelope: Option<(ArtifactSource, Location<N, D, G>)>,
67}
68impl<'c, 'd, N: Copy, D: Copy, G: Copy> StatusRoleComparison<'c, 'd, N, D, G> {
69 #[must_use]
71 pub fn artifact(&self) -> ArtifactRef<'c, 'd> {
72 self.artifact
73 }
74 #[must_use]
76 pub fn target(&self) -> &'c Certificate<'d> {
77 self.target
78 }
79 #[must_use]
81 pub fn kind(&self) -> ExclusiveFindingKind {
82 self.kind
83 }
84 #[must_use]
86 pub fn agreement(&self) -> StatusRoleAgreement {
87 self.agreement
88 }
89 #[must_use]
92 pub fn envelope(&self) -> Option<(ArtifactSource, Location<N, D, G>)> {
93 self.envelope
94 }
95}
96
97pub struct StatusRoleRow<N> {
99 property: usize,
100 entry: usize,
101 node: N,
102 search: ReferenceSearch,
103 range: Range<usize>,
104 root_range: Range<usize>,
105 credential_range: Range<usize>,
106 role: unsigned::Role,
107}
108impl<N: Copy> StatusRoleRow<N> {
109 #[must_use]
111 pub fn property(&self) -> usize {
112 self.property
113 }
114 #[must_use]
116 pub fn entry(&self) -> usize {
117 self.entry
118 }
119 #[must_use]
121 pub fn node(&self) -> N {
122 self.node
123 }
124 #[must_use]
126 pub fn search(&self) -> ReferenceSearch {
127 self.search
128 }
129 #[must_use]
131 pub fn role(&self) -> unsigned::Role {
132 self.role
133 }
134}
135
136pub struct RevocationRoles<'prepared, 'certificate, 'document, N, D, G> {
140 prepared: &'prepared Preparation<N, D, G>,
141 rows: OwnedVec<StatusRoleRow<N>>,
142 comparisons: OwnedVec<StatusRoleComparison<'certificate, 'document, N, D, G>>,
143 root_sources: OwnedVec<PathStatusSource<'certificate, 'document>>,
144 root_comparisons: OwnedVec<StatusSourceComparison>,
145 root_present: bool,
146 credential_sources: OwnedVec<CredentialStatusSource<'certificate, 'document>>,
147 credential_comparisons: OwnedVec<StatusSourceComparison>,
148 credential_present: bool,
149}
150impl<'p, 'c, 'd, N: Copy, D, G> RevocationRoles<'p, 'c, 'd, N, D, G> {
151 #[must_use]
153 pub fn prepared(&self) -> &'p Preparation<N, D, G> {
154 self.prepared
155 }
156 #[must_use]
158 pub fn rows(&self) -> &[StatusRoleRow<N>] {
159 &self.rows
160 }
161 #[must_use]
163 pub fn comparisons(&self, row: usize) -> Option<&[StatusRoleComparison<'c, 'd, N, D, G>]> {
164 self.comparisons.get(self.rows.get(row)?.range.clone())
165 }
166 #[must_use]
170 pub fn root_sources(&self) -> &[PathStatusSource<'c, 'd>] {
171 &self.root_sources
172 }
173 #[must_use]
175 pub fn root_comparisons(&self, row: usize) -> Option<&[StatusSourceComparison]> {
176 self.root_comparisons
177 .get(self.rows.get(row)?.root_range.clone())
178 }
179 #[must_use]
183 pub fn all_root_sources_referenced(&self) -> bool {
184 self.root_present
185 }
186 #[must_use]
190 pub fn credential_sources(&self) -> &[CredentialStatusSource<'c, 'd>] {
191 &self.credential_sources
192 }
193 #[must_use]
195 pub fn credential_comparisons(&self, row: usize) -> Option<&[StatusSourceComparison]> {
196 self.credential_comparisons
197 .get(self.rows.get(row)?.credential_range.clone())
198 }
199 #[must_use]
203 pub fn all_credential_sources_referenced(&self) -> bool {
204 self.credential_present
205 }
206}
207
208pub type RevocationRoleResult<'p, 'c, 'd, X, PE> = binary::BinaryResult<
210 RevocationRoles<'p, 'c, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
211 X,
212 CertificateRoleError<'c, 'd, PE>,
213>;
214
215#[derive(Clone, Copy)]
216struct Envelope<'source, N, D, G> {
217 bytes: &'source [u8],
218 origin: ArtifactSource,
219 location: Location<N, D, G>,
220}
221
222type EnvelopeIndex<'s, N, D, G, W> = OwnedMap<&'s [u8], Envelope<'s, N, D, G>, W>;
223type XmlEnvelope<'s, X> =
224 Envelope<'s, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
225type XmlEnvelopeIndex<'s, X, W> =
226 EnvelopeIndex<'s, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, W>;
227type ArtifactAnswer<'s, X> = (StatusRoleAgreement, Option<XmlEnvelope<'s, X>>);
228type OriginalRow<'a, X> = (
229 usize,
230 &'a super::ReferenceProperty<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
231 usize,
232);
233type StatusResult<'c, 'd, T, E> = Result<T, digest::Error<CertificateRoleError<'c, 'd, E>, E>>;
234type LocatedEnvelopes<'s, X, W> = OwnedMap<usize, Option<XmlEnvelope<'s, X>>, W>;
235type RoleComparisons<'c, 'd, X, W> = OwnedVec<
236 StatusRoleComparison<'c, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
237 W,
238>;
239
240fn insert_envelope<'source, N: Copy, D: Copy, G: Copy, W: Work>(
241 index: &mut OwnedMap<&'source [u8], Envelope<'source, N, D, G>, W>,
242 response: &structured_pkix::ocsp::ResponseBytes<'source>,
243 origin: ArtifactSource,
244 location: Location<N, D, G>,
245 work: &W,
246) -> Result<(), MemoryError> {
247 let Some(payload) = basic_payload(response, work)? else {
248 return Ok(());
249 };
250 if index.get(&payload, work)?.is_none() {
251 index.try_insert(
252 payload,
253 Envelope {
254 bytes: response.as_bytes(),
255 origin,
256 location,
257 },
258 )?;
259 }
260 Ok(())
261}
262
263fn envelope_index<'source, N: Copy, D: Copy, G: Copy, W: Work>(
264 pools: CandidatePools<'_, 'source, N, D, G>,
265 work: &W,
266) -> Result<EnvelopeIndex<'source, N, D, G, W>, MemoryError> {
267 let mut index = work.map();
268 for (ordinal, value) in pools.xml.values().iter().enumerate() {
269 work.charge_work(1)?;
270 if let material::Value::Ocsp(Response::Successful(response)) = &value.value {
271 insert_envelope(
272 &mut index,
273 response,
274 ArtifactSource::Material(ordinal),
275 value.location,
276 work,
277 )?;
278 }
279 }
280 if let Some(tokens) = pools.tokens {
281 for response in tokens.responses() {
282 work.charge_work(1)?;
283 let source = &tokens.view.sources()[response.origin.token];
284 insert_envelope(
285 &mut index,
286 &response.value,
287 ArtifactSource::TokenRevocation(response.origin),
288 source.location(),
289 work,
290 )?;
291 }
292 }
293 Ok(index)
294}
295
296struct StatusComparisons<
297 'provider,
298 'source,
299 'certificate,
300 'document,
301 X: Xml,
302 P: DigestProvider<'provider, X>,
303 W: Work,
304> {
305 memo: Memo<'provider, 'source, P, W>,
306 candidates: Option<OwnedVec<ReferenceStatus<'certificate, 'document>, W>>,
307 envelopes: Option<XmlEnvelopeIndex<'source, X, W>>,
308 located: LocatedEnvelopes<'source, X, W>,
309 comparisons: RoleComparisons<'certificate, 'document, X, W>,
310 required: Option<Required<'certificate, 'document, W>>,
311 root_comparisons: OwnedVec<StatusSourceComparison, W>,
312 credential_required: Option<CredentialRequired<'certificate, 'document, W>>,
313 credential_comparisons: OwnedVec<StatusSourceComparison, W>,
314 attribute_requested: bool,
315 covered: OwnedVec<bool, W>,
316 crl_ids: OwnedMap<&'document [u8], usize, W>,
317 ocsp_ids: OwnedMap<&'document [u8], usize, W>,
318 next_id: usize,
319}
320impl<'provider, 'source, 'c, 'd, X: Xml, P: DigestProvider<'provider, X>, W: Work>
321 StatusComparisons<'provider, 'source, 'c, 'd, X, P, W>
322{
323 fn new(provider: &'provider P, work: &W) -> Self {
324 Self {
325 memo: Memo::new(provider, work),
326 candidates: None,
327 envelopes: None,
328 located: work.map(),
329 comparisons: work.vector(),
330 required: None,
331 root_comparisons: work.vector(),
332 credential_required: None,
333 credential_comparisons: work.vector(),
334 attribute_requested: false,
335 covered: work.vector(),
336 crl_ids: work.map(),
337 ocsp_ids: work.map(),
338 next_id: 0,
339 }
340 }
341 fn envelope(
342 &mut self,
343 ordinal: usize,
344 body: &[u8],
345 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
346 work: &W,
347 ) -> Result<Option<XmlEnvelope<'source, X>>, MemoryError> {
348 if let Some(&found) = self.located.get(&ordinal, work)? {
349 return Ok(found);
350 }
351 if self.envelopes.is_none() {
352 self.envelopes = Some(envelope_index(pools, work)?);
353 }
354 let found = self
355 .envelopes
356 .as_ref()
357 .expect("initialized envelope index")
358 .get(&body, work)?
359 .copied();
360 self.located.try_insert(ordinal, found)?;
361 Ok(found)
362 }
363 fn agreement(
364 &mut self,
365 artifact: ArtifactRef<'c, 'd>,
366 method: usize,
367 expected: &[u8],
368 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
369 work: &W,
370 ) -> StatusResult<'c, 'd, ArtifactAnswer<'source, X>, P::Error> {
371 let (bytes, ids) = match artifact {
372 ArtifactRef::Crl(crl) => (crl.as_bytes(), &mut self.crl_ids),
373 ArtifactRef::Ocsp(basic) => (basic.as_bytes(), &mut self.ocsp_ids),
374 ArtifactRef::Certificate(_) => unreachable!("private status candidate"),
375 };
376 let ordinal =
377 if let Some(&ordinal) = ids.get(&bytes, work).map_err(digest::Error::Resource)? {
378 ordinal
379 } else {
380 let ordinal = self.next_id;
381 self.next_id = ordinal
382 .checked_add(1)
383 .ok_or(digest::Error::Resource(MemoryError::WorkExceeded))?;
384 ids.try_insert(bytes, ordinal)
385 .map_err(digest::Error::Resource)?;
386 ordinal
387 };
388 let envelope = match artifact {
389 ArtifactRef::Ocsp(_) => self
390 .envelope(ordinal, bytes, pools, work)
391 .map_err(digest::Error::Resource)?,
392 _ => None,
393 };
394 let bytes = match artifact {
395 ArtifactRef::Crl(_) => Some(bytes),
396 _ => envelope.map(|value| value.bytes),
397 };
398 let agreement = if let Some(bytes) = bytes {
399 StatusRoleAgreement::Compared(
400 self.memo
401 .compare(ordinal, bytes, method, expected, work)
402 .map_err(|cause| digest::Error::Source(CertificateRoleError::Digest(cause)))?,
403 )
404 } else {
405 StatusRoleAgreement::EnvelopeUnprovided
406 };
407 Ok((agreement, envelope))
408 }
409 fn compare_root(
410 &mut self,
411 kind: ReferenceKind,
412 method: usize,
413 expected: &[u8],
414 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
415 work: &W,
416 ) -> Result<(), digest::Error<CertificateRoleError<'c, 'd, P::Error>, P::Error>> {
417 let count = self
418 .required
419 .as_ref()
420 .expect("prepared root requirements")
421 .sources
422 .len();
423 for ordinal in 0..count {
424 work.charge_work(1).map_err(digest::Error::Resource)?;
425 let artifact = self.required.as_ref().expect("root sources").sources[ordinal].artifact;
426 if !compatible(kind, artifact) {
427 continue;
428 }
429 self.root_comparisons
430 .try_reserve(1)
431 .map_err(digest::Error::Resource)?;
432 let (agreement, _) = self.agreement(artifact, method, expected, pools, work)?;
433 if agreement == StatusRoleAgreement::Compared(Agreement::Matches) {
434 self.covered[ordinal] = true;
435 }
436 self.root_comparisons
437 .try_push(StatusSourceComparison {
438 source: ordinal,
439 agreement,
440 })
441 .map_err(digest::Error::Resource)?;
442 }
443 Ok(())
444 }
445 fn compare(
446 &mut self,
447 selection: (ReferenceKind, unsigned::Role),
448 identity: FormatDigest<P::Identity>,
449 expected: &[u8],
450 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
451 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
452 work: &W,
453 ) -> Result<(), digest::Error<CertificateRoleError<'c, 'd, P::Error>, P::Error>> {
454 let (kind, role) = selection;
455 let method = self
456 .memo
457 .method(identity, work)
458 .map_err(|cause| digest::Error::Source(CertificateRoleError::Digest(cause)))?;
459 self.compare_credentials((kind, role), method, expected, artifacts, pools, work)?;
460 if role != unsigned::Role::RevocationReferences {
461 return Ok(());
462 }
463 if self.required.is_none() {
464 let required =
465 ref_status_required::collect(artifacts, work).map_err(digest::Error::Resource)?;
466 self.covered
467 .try_reserve_exact(required.sources.len())
468 .map_err(digest::Error::Resource)?;
469 for _ in &required.sources {
470 self.covered
471 .try_push(false)
472 .map_err(digest::Error::Resource)?;
473 }
474 self.required = Some(required);
475 }
476 self.compare_root(kind, method, expected, pools, work)?;
477 if self.candidates.is_none() {
478 self.candidates = Some(
479 exclusivity::reference_status_candidates(artifacts, work).map_err(|cause| {
480 digest::Error::Source(match cause {
481 CaError::Resource(cause) => CertificateRoleError::Resource(cause),
482 CaError::Certificate(certificate, cause) => {
483 CertificateRoleError::Certificate { certificate, cause }
484 }
485 })
486 })?,
487 );
488 }
489 let count = self
490 .candidates
491 .as_ref()
492 .expect("initialized actual status candidates")
493 .len();
494 for ordinal in 0..count {
495 work.charge_work(1).map_err(digest::Error::Resource)?;
496 let candidate = &self.candidates.as_ref().expect("initialized candidates")[ordinal];
497 let artifact = candidate.artifact;
498 let target = candidate.certificate;
499 let ca = candidate.fact;
500 if !compatible(kind, artifact) {
501 continue;
502 }
503 self.comparisons
504 .try_reserve(1)
505 .map_err(digest::Error::Resource)?;
506 let (agreement, envelope) = self.agreement(artifact, method, expected, pools, work)?;
507 let kind = match ca {
508 CaFact::Asserted => ExclusiveFindingKind::ForbiddenCa,
509 CaFact::Unstated => ExclusiveFindingKind::UnstatedCa,
510 CaFact::NotAsserted => unreachable!("non-CA is not an exclusion candidate"),
511 };
512 self.comparisons
513 .try_push(StatusRoleComparison {
514 artifact,
515 target,
516 kind,
517 agreement,
518 envelope: envelope.map(|value| (value.origin, value.location)),
519 })
520 .map_err(digest::Error::Resource)?;
521 }
522 Ok(())
523 }
524 fn compare_credentials(
525 &mut self,
526 (kind, role): (ReferenceKind, unsigned::Role),
527 method: usize,
528 expected: &[u8],
529 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
530 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
531 work: &W,
532 ) -> StatusResult<'c, 'd, (), P::Error> {
533 if !self.attribute_requested {
534 return Ok(());
535 }
536 if self.credential_required.is_none() {
537 self.credential_required = Some(
538 ref_status_required::collect_credentials(artifacts, work)
539 .map_err(digest::Error::Resource)?,
540 );
541 }
542 let count = self
543 .credential_required
544 .as_ref()
545 .expect("collected credential statuses")
546 .sources
547 .len();
548 for ordinal in 0..count {
549 work.charge_work(1).map_err(digest::Error::Resource)?;
550 let artifact = self
551 .credential_required
552 .as_ref()
553 .expect("credential statuses")
554 .sources[ordinal]
555 .source
556 .artifact;
557 if !compatible(kind, artifact) {
558 continue;
559 }
560 self.credential_comparisons
561 .try_reserve(1)
562 .map_err(digest::Error::Resource)?;
563 let (agreement, _) = self.agreement(artifact, method, expected, pools, work)?;
564 if agreement == StatusRoleAgreement::Compared(Agreement::Matches) {
565 let source = &mut self
566 .credential_required
567 .as_mut()
568 .expect("credential statuses")
569 .sources[ordinal];
570 match role {
571 unsigned::Role::RevocationReferences => source.ordinary = true,
572 unsigned::Role::AttributeRevocationReferences => source.attribute = true,
573 _ => unreachable!("private revocation property roles"),
574 }
575 }
576 self.credential_comparisons
577 .try_push(StatusSourceComparison {
578 source: ordinal,
579 agreement,
580 })
581 .map_err(digest::Error::Resource)?;
582 }
583 Ok(())
584 }
585 fn row(
586 &mut self,
587 xml: &X,
588 (property, refs, entry): OriginalRow<'_, X>,
589 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
590 pools: CandidatePools<'_, 'source, X::Id, X::Document, X::Generation>,
591 provider: &'provider P,
592 work: &W,
593 ) -> binary::BinaryResult<StatusRoleRow<X::Id>, X, CertificateRoleError<'c, 'd, P::Error>>
594 where
595 X: BinaryXml,
596 {
597 let reference = &refs.entries()[entry];
598 let start = self.comparisons.len();
599 let root_start = self.root_comparisons.len();
600 let credential_start = self.credential_comparisons.len();
601 let search = if reference.kind() == ReferenceKind::Other {
602 ReferenceSearch::OtherFormat
603 } else if let Some((method, value)) = reference.digest() {
604 match digest::check_with(
605 xml,
606 digest::Reference {
607 snapshot: refs.snapshot(),
608 method,
609 value,
610 },
611 provider,
612 work,
613 |identity, expected, work| {
614 self.compare(
615 (reference.kind(), refs.role()),
616 identity,
617 expected,
618 artifacts,
619 pools,
620 work,
621 )
622 },
623 )
624 .map_err(|cause| cause.map_callback(role_error))?
625 {
626 digest::Checked::Value(()) => ReferenceSearch::Compared,
627 digest::Checked::Answer(answer) => ReferenceSearch::Unavailable(answer),
628 }
629 } else {
630 ReferenceSearch::IdentifierOnly
631 };
632 Ok(StatusRoleRow {
633 property,
634 entry,
635 node: reference.node(),
636 search,
637 role: refs.role(),
638 range: start..self.comparisons.len(),
639 root_range: root_start..self.root_comparisons.len(),
640 credential_range: credential_start..self.credential_comparisons.len(),
641 })
642 }
643}
644
645fn compatible(kind: ReferenceKind, artifact: ArtifactRef<'_, '_>) -> bool {
646 matches!(
647 (kind, artifact),
648 (ReferenceKind::Crl, ArtifactRef::Crl(_)) | (ReferenceKind::Ocsp, ArtifactRef::Ocsp(_))
649 )
650}
651
652pub fn check_revocation_reference_roles<
678 'p,
679 'c,
680 'd,
681 'q,
682 X: BinaryXml,
683 P: DigestProvider<'q, X>,
684 W: Work,
685>(
686 xml: &X,
687 prepared: &'p Prepared<X>,
688 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, X::Id, X::Document, X::Generation, Xades>,
689 view: &material::View<'_, X::Id, X::Document, X::Generation>,
690 tokens: Option<&CarriedMaterial<'_, X::Id, X::Document, X::Generation>>,
691 provider: &'q P,
692 work: &W,
693) -> RevocationRoleResult<'p, 'c, 'd, X, P::Error> {
694 let source = prepared.bound().containers();
695 let location = Location {
696 snapshot: source.snapshot(),
697 node: source.signature(),
698 };
699 let access = Access::new(xml, location.snapshot, work);
700 access
701 .query(location.node, |_, _, _| Ok(()))
702 .map_err(|cause| binary::Error::Access { location, cause })?;
703 let failed = |cause| binary::Error::Callback { location, cause };
704 let resource = |cause| failed(CertificateRoleError::Resource(cause));
705 let required = artifacts.inputs().requirements();
706 let pools = CandidatePools { xml: view, tokens };
707 if required.scope().location().snapshot != location.snapshot
708 || !matches!(required.scope(), TimestampScope::All(_))
709 || !access
710 .equal_id(required.signature(), location.node)
711 .map_err(resource)?
712 || !pools.same_source(location, &access).map_err(resource)?
713 {
714 return Err(failed(CertificateRoleError::Scope));
715 }
716 let mut state = StatusComparisons::<X, P, W>::new(provider, work);
717 for property in prepared.validation_references() {
718 work.charge_work(1).map_err(resource)?;
719 state.attribute_requested |=
720 property.role() == unsigned::Role::AttributeRevocationReferences;
721 }
722 let mut rows = work.vector();
723 let mut root_present = true;
724 for (property, refs) in prepared.validation_references().iter().enumerate() {
725 work.charge_work(1).map_err(resource)?;
726 if !matches!(
727 refs.role(),
728 unsigned::Role::RevocationReferences | unsigned::Role::AttributeRevocationReferences
729 ) {
730 continue;
731 }
732 if refs.role() == unsigned::Role::RevocationReferences {
733 for covered in &mut state.covered {
734 work.charge_work(1).map_err(resource)?;
735 *covered = false;
736 }
737 }
738 for entry in 0..refs.entries().len() {
739 work.charge_work(1).map_err(resource)?;
740 let row = state.row(
741 xml,
742 (property, refs, entry),
743 artifacts,
744 pools,
745 provider,
746 work,
747 )?;
748 rows.try_push(row).map_err(resource)?;
749 }
750 if refs.role() == unsigned::Role::AttributeRevocationReferences {
751 continue;
752 }
753 if let Some(required) = &state.required {
754 root_present &= !required.missing_input;
755 for &covered in &state.covered {
756 work.charge_work(1).map_err(resource)?;
757 root_present &= covered;
758 }
759 } else {
760 root_present = false;
761 }
762 }
763 let mut credential_present = !state.attribute_requested;
764 if let Some(required) = &state.credential_required {
765 credential_present = !required.missing_input;
766 for source in &required.sources {
767 work.charge_work(1).map_err(resource)?;
768 credential_present &= source.ordinary || source.attribute;
769 }
770 }
771 access
772 .query(location.node, |_, _, _| Ok(()))
773 .map_err(|cause| binary::Error::Access { location, cause })?;
774 Ok(RevocationRoles {
775 prepared,
776 rows: rows.into_owned(),
777 comparisons: state.comparisons.into_owned(),
778 root_sources: state.required.map_or_else(
779 || work.vector().into_owned(),
780 |required| required.sources.into_owned(),
781 ),
782 root_comparisons: state.root_comparisons.into_owned(),
783 root_present,
784 credential_sources: state.credential_required.map_or_else(
785 || work.vector().into_owned(),
786 |required| required.sources.into_owned(),
787 ),
788 credential_comparisons: state.credential_comparisons.into_owned(),
789 credential_present,
790 })
791}