structured_xades/validation/
ref_status_required.rs1use crate::Xades;
4use structured_asn1::memory::{MemoryError, OwnedVec, Work};
5use structured_ltv::material::{
6 ArtifactRef, ArtifactUses,
7 status::{self, StatusPart},
8};
9use structured_ltv::pki::{Participant, ParticipantAuthority, PathStatuses};
10
11pub struct PathStatusSource<'certificate, 'document> {
13 pub(super) artifact: ArtifactRef<'certificate, 'document>,
14 member: usize,
15 contributor: usize,
16}
17impl<'c, 'd> PathStatusSource<'c, 'd> {
18 #[must_use]
21 pub fn artifact(&self) -> ArtifactRef<'c, 'd> {
22 self.artifact
23 }
24 #[must_use]
26 pub fn member(&self) -> usize {
27 self.member
28 }
29 #[must_use]
31 pub fn contributor(&self) -> usize {
32 self.contributor
33 }
34}
35
36pub(super) struct Required<'c, 'd, W: Work> {
37 pub(super) sources: OwnedVec<PathStatusSource<'c, 'd>, W>,
38 pub(super) missing_input: bool,
39}
40pub(super) fn collect<'c, 'd, N: Copy, D: Copy, G: Copy, W: Work>(
41 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, N, D, G, Xades>,
42 work: &W,
43) -> Result<Required<'c, 'd, W>, MemoryError> {
44 work.charge_work(1)?;
45 let inputs = artifacts.inputs();
46 let root = inputs
47 .requirements()
48 .entries()
49 .first()
50 .expect("constructed signer requirement");
51 assert!(
52 matches!(root.participant(), Participant::Signer(_)),
53 "signer is first"
54 );
55 let mut sources = work.vector();
56 let mut missing_input = false;
57 if let ParticipantAuthority::Path(path) = root.authority() {
58 let statuses = inputs
59 .authorities()
60 .roots()
61 .first()
62 .expect("signer status root");
63 assert!(
64 core::ptr::eq(statuses.path(), path),
65 "constructed root association"
66 );
67 missing_input = append_path(statuses, work, |source| sources.try_push(source))?;
68 }
69 Ok(Required {
70 sources,
71 missing_input,
72 })
73}
74
75fn append_path<'c, 'd, W: Work>(
78 statuses: &PathStatuses<'_, 'c, 'd>,
79 work: &W,
80 mut record: impl FnMut(PathStatusSource<'c, 'd>) -> Result<(), MemoryError>,
81) -> Result<bool, MemoryError> {
82 let mut missing_input = false;
83 for (member, entry) in statuses.entries().iter().enumerate() {
84 work.charge_work(1)?;
85 let Some(source) = status::member_source(entry) else {
86 missing_input = true;
89 continue;
90 };
91 status::visit_artifacts_in(source, work, |artifact, contributor, part| {
95 if part != StatusPart::SigningCertificate {
96 record(PathStatusSource {
97 artifact,
98 member,
99 contributor,
100 })?;
101 }
102 Ok::<_, MemoryError>(())
103 })?;
104 }
105 Ok(missing_input)
106}
107
108pub struct CredentialStatusSource<'c, 'd> {
110 participant: usize,
111 pub(super) source: PathStatusSource<'c, 'd>,
112 pub(super) ordinary: bool,
113 pub(super) attribute: bool,
114}
115impl<'c, 'd> CredentialStatusSource<'c, 'd> {
116 #[must_use]
118 pub fn participant(&self) -> usize {
119 self.participant
120 }
121 #[must_use]
123 pub fn source(&self) -> &PathStatusSource<'c, 'd> {
124 &self.source
125 }
126 #[must_use]
128 pub fn presence(&self) -> (bool, bool) {
129 (self.ordinary, self.attribute)
130 }
131 #[must_use]
133 pub fn redundant_attribute_reference(&self) -> bool {
134 self.ordinary && self.attribute
135 }
136}
137
138pub(super) struct CredentialRequired<'c, 'd, W: Work> {
139 pub(super) sources: OwnedVec<CredentialStatusSource<'c, 'd>, W>,
140 pub(super) missing_input: bool,
141}
142
143pub(super) fn collect_credentials<'c, 'd, N: Copy, D: Copy, G: Copy, W: Work>(
144 artifacts: &ArtifactUses<'_, '_, '_, 'c, 'd, N, D, G, Xades>,
145 work: &W,
146) -> Result<CredentialRequired<'c, 'd, W>, MemoryError> {
147 let inputs = artifacts.inputs();
148 let mut roots = inputs.authorities().roots().iter();
149 let mut sources = work.vector();
150 let mut missing_input = false;
151 for (participant, entry) in inputs.requirements().entries().iter().enumerate() {
152 work.charge_work(1)?;
153 let ParticipantAuthority::Path(path) = entry.authority() else {
154 continue;
155 };
156 let statuses = roots
159 .next()
160 .expect("constructed complete path/status pairing");
161 assert!(
162 core::ptr::eq(statuses.path(), path),
163 "same constructed path receipt"
164 );
165 if !matches!(entry.participant(), Participant::Credential { .. }) {
166 continue;
167 }
168 missing_input |= append_path(statuses, work, |source| {
171 sources.try_push(CredentialStatusSource {
172 participant,
173 source,
174 ordinary: false,
175 attribute: false,
176 })
177 })?;
178 }
179 Ok(CredentialRequired {
180 sources,
181 missing_input,
182 })
183}