Skip to main content

structured_ltv/material/
uses.rs

1//! Exact artifact uses across all reached actors, independent of path receipts.
2
3use super::ArtifactRef;
4use super::index::Kind;
5use super::status::{ArtifactVisitError, member_source, visit_artifacts};
6use crate::pki::{
7    AuthorityClosureError, AuthorityUses, ParticipantAuthority, ParticipantClosure, Uses, UsesError,
8};
9use crate::source::{Query, ScopeError};
10use structured_asn1::memory::{HashWork, MapExecution, MemoryError, OwnedMap, OwnedVec, Work};
11
12/// An original certificate/status value with ALL its actual actor purposes.
13/// Exact kind and original DER identify an artifact; two checked paths or
14/// separately parsed copies do not make the same certificate exclusive to one.
15pub struct ArtifactUse<'certificate, 'document> {
16    artifact: ArtifactRef<'certificate, 'document>,
17    uses: Uses,
18    targets: Option<usize>,
19    next_identity: Option<usize>,
20}
21
22impl<'certificate, 'document> ArtifactUse<'certificate, 'document> {
23    /// First original value carrying this exact kind/DER identity.
24    #[must_use]
25    pub fn artifact(&self) -> ArtifactRef<'certificate, 'document> {
26        self.artifact
27    }
28    /// Union of every reached actor's uses, including status-authority uses.
29    #[must_use]
30    pub fn uses(&self) -> Uses {
31        self.uses
32    }
33}
34
35/// Complete X.509/CRL/OCSP artifact census for the same participant requirements.
36/// Credential-format-specific data remain separate. This does not assert CA
37/// status, allowed placement, freshness or baseline grade.
38pub struct ArtifactUses<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, I> {
39    inputs: &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
40    entries: OwnedVec<ArtifactUse<'certificate, 'document>>,
41    targets: OwnedVec<StatusTarget>,
42}
43
44// A status artifact applies to THIS certificate, not its authentication signer.
45// Both ordinals refer to the same immutable artifact catalogue; no source bytes
46// or parsed certificates are copied to preserve this relation.
47struct StatusTarget {
48    certificate: usize,
49    next: Option<usize>,
50}
51
52impl<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, I>
53    ArtifactUses<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, I>
54{
55    /// Same source-derived actor/status input closure, without another query.
56    #[must_use]
57    pub fn inputs(
58        &self,
59    ) -> &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>
60    {
61        self.inputs
62    }
63    /// Every distinct artifact once in first-encounter order.
64    #[must_use]
65    pub fn entries(&self) -> &[ArtifactUse<'certificate, 'document>] {
66        &self.entries
67    }
68    /// The certificates whose status the CRL/OCSP entry at `ordinal` reports,
69    /// as ordinals into [`Self::entries`]; empty for a certificate entry.
70    /// Each step is constant time; a caller paying per target admits it.
71    ///
72    /// # Panics
73    /// When `ordinal` is not an ordinal of [`Self::entries`].
74    #[must_use]
75    pub fn targets(&self, ordinal: usize) -> Targets<'_> {
76        Targets {
77            targets: &self.targets,
78            next: self.entries[ordinal].targets,
79        }
80    }
81}
82
83/// Status targets of one artifact, most recently recorded first.
84pub struct Targets<'a> {
85    targets: &'a [StatusTarget],
86    next: Option<usize>,
87}
88
89impl Iterator for Targets<'_> {
90    type Item = usize;
91    fn next(&mut self) -> Option<usize> {
92        let target = &self.targets[self.next?];
93        self.next = target.next;
94        Some(target.certificate)
95    }
96}
97
98impl core::iter::FusedIterator for Targets<'_> {}
99
100/// Original source/role association and resource failures remain terminal.
101pub type ArtifactUsesResult<'closure, 'requirements, 'view, 'certificate, 'document, Q, I> = Result<
102    ArtifactUses<
103        'closure,
104        'requirements,
105        'view,
106        'certificate,
107        'document,
108        <Q as Query>::Id,
109        <Q as Query>::Document,
110        <Q as Query>::Generation,
111        I,
112    >,
113    UsesError<<Q as Query>::Id>,
114>;
115
116#[derive(Clone, Copy, PartialEq, Eq, Hash)]
117struct Bucket {
118    kind: Kind,
119    hash: u64,
120}
121impl HashWork for Bucket {
122    fn hash_work(&self) -> Result<u64, MemoryError> {
123        Ok(2)
124    }
125}
126
127struct Census<'certificate, 'document, W: Work> {
128    index: Option<OwnedMap<Bucket, usize, W>>,
129    entries: OwnedVec<ArtifactUse<'certificate, 'document>, W>,
130    targets: OwnedVec<StatusTarget, W>,
131}
132
133impl<'certificate, 'document, W: Work> Census<'certificate, 'document, W> {
134    fn record(
135        &mut self,
136        artifact: ArtifactRef<'certificate, 'document>,
137        uses: Uses,
138        work: &W,
139    ) -> Result<usize, MemoryError> {
140        let scoped = artifact.execution_in(work);
141        scoped.charge_work(1)?;
142        let key = artifact.identity();
143        let hash = self
144            .index
145            .as_ref()
146            .expect("census map restored before every visit")
147            .fingerprint(&key, &scoped)?;
148        self.record_hashed(
149            artifact,
150            uses,
151            work,
152            Bucket {
153                kind: key.kind,
154                hash,
155            },
156        )
157    }
158
159    fn record_hashed(
160        &mut self,
161        artifact: ArtifactRef<'certificate, 'document>,
162        uses: Uses,
163        work: &W,
164        key: Bucket,
165    ) -> Result<usize, MemoryError> {
166        let scoped = artifact.execution_in(work);
167        scoped.charge_work(1)?;
168        let head = self
169            .index
170            .as_ref()
171            .expect("census map restored before every visit")
172            .get(&key, &scoped)?
173            .copied();
174        let mut next = head;
175        while let Some(index) = next {
176            scoped.charge_work(1)?;
177            let stored = self.entries[index].artifact;
178            let original = stored.execution_in(work);
179            original.charge_work(1)?;
180            let first = artifact.identity().bytes;
181            let second = stored.identity().bytes;
182            let equal = if core::ptr::eq(first, second) {
183                true
184            } else if first.len() != second.len() {
185                false
186            } else {
187                // Each actual source admits its byte scan, while the
188                // same root operation funds both operands. Cached bucket
189                // metadata never rehashes another source's borrowed DER.
190                let length = u64::try_from(first.len()).map_err(|_| MemoryError::WorkExceeded)?;
191                scoped.charge_work(length)?;
192                original.charge_work(length)?;
193                first == second
194            };
195            if equal {
196                self.entries[index].uses.merge(uses);
197                return Ok(index);
198            }
199            next = self.entries[index].next_identity;
200        }
201        let index = self.entries.len();
202        let original = self
203            .index
204            .take()
205            .expect("census map restored before every visit");
206        let (mut table, previous) = match original.rebind(scoped) {
207            MapExecution::Changed { map, previous } => (map, previous),
208            MapExecution::Foreign { map, .. } => {
209                self.index = Some(map);
210                unreachable!(
211                    "one private census is always mutated under its original query account"
212                )
213            }
214        };
215        let inserted = table.try_insert(key, index);
216        self.index = Some(match table.rebind(previous) {
217            MapExecution::Changed { map, .. } => map,
218            MapExecution::Foreign { .. } => {
219                unreachable!("restoration returns the exact previous execution owner")
220            }
221        });
222        inserted?;
223        self.entries.try_push(ArtifactUse {
224            artifact,
225            uses,
226            targets: None,
227            next_identity: head,
228        })?;
229        Ok(index)
230    }
231}
232
233/// Merge purposes by exact original artifact identity AFTER role propagation.
234/// EN319132-1 ยงยง5.4.2(5)/5.4.3(4) concern exclusive certificate/status uses,
235/// which cannot be inferred from path identity or the first occurrence alone.
236/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
237///
238/// Reuses the archive index's key/hash admission and status source visitor.
239/// Complete/delta contributors, actual OCSP/CRL signers and existing direct
240/// anchors participate. No parse, provider, signature or normalization repeats.
241/// Current Work pays even when the original role census used another account;
242/// partial metadata and scratch index are released on every refusal.
243pub fn artifact_uses<'closure, 'requirements, 'view, 'certificate, 'document, Q: Query, I>(
244    authorities: &AuthorityUses<
245        'closure,
246        'requirements,
247        'view,
248        'certificate,
249        'document,
250        Q::Id,
251        Q::Document,
252        Q::Generation,
253        I,
254    >,
255    query: &Q,
256) -> ArtifactUsesResult<'closure, 'requirements, 'view, 'certificate, 'document, Q, I> {
257    let inputs = authorities.inputs();
258    let requirements = inputs.requirements();
259    if requirements.scope().location().snapshot != query.snapshot() {
260        return Err(UsesError::Foreign);
261    }
262    query
263        .admit(requirements.signature(), 1)
264        .map_err(UsesError::Scope)?;
265    let work = query.execution();
266    let resource = |cause| UsesError::Closure(AuthorityClosureError::Resource(cause));
267    let mut census = Census {
268        index: Some(work.map()),
269        entries: work.vector(),
270        targets: work.vector(),
271    };
272    for authority in authorities.entries() {
273        work.charge_work(1).map_err(resource)?;
274        let path = authority.node().statuses.path();
275        let uses = authority.uses();
276        if let Some(certificate) = path.anchor().certificate() {
277            census
278                .record(ArtifactRef::Certificate(certificate), uses, work)
279                .map_err(resource)?;
280        }
281        for member in path.members() {
282            census
283                .record(ArtifactRef::Certificate(member.certificate()), uses, work)
284                .map_err(resource)?;
285        }
286        for input in authority.node().statuses.entries() {
287            work.charge_work(1).map_err(resource)?;
288            let Some(source) = member_source(input) else {
289                continue;
290            };
291            let certificate = census
292                .record(ArtifactRef::Certificate(source.target()), uses, work)
293                .map_err(resource)?;
294            visit_artifacts(source, requirements.signature(), query, |artifact, _, _| {
295                let index = census.record(artifact, uses, work)?;
296                if !matches!(artifact, ArtifactRef::Certificate(_)) {
297                    let next = census.entries[index].targets;
298                    let target = census.targets.len();
299                    census
300                        .targets
301                        .try_push(StatusTarget { certificate, next })?;
302                    census.entries[index].targets = Some(target);
303                }
304                Ok::<_, MemoryError>(())
305            })
306            .map_err(|cause| match cause {
307                ArtifactVisitError::Scope(ScopeError::Resource { cause, .. })
308                | ArtifactVisitError::Visitor(cause) => resource(cause),
309                ArtifactVisitError::Scope(cause) => UsesError::Scope(cause),
310            })?;
311        }
312    }
313    for entry in requirements.entries() {
314        work.charge_work(1).map_err(resource)?;
315        let ParticipantAuthority::Anchor(anchor) = entry.authority() else {
316            continue;
317        };
318        if let Some(certificate) = anchor.anchor().certificate() {
319            census
320                .record(
321                    ArtifactRef::Certificate(certificate),
322                    Uses::participant(entry.participant()),
323                    work,
324                )
325                .map_err(resource)?;
326        }
327    }
328    Ok(ArtifactUses {
329        inputs,
330        entries: census.entries.into_owned(),
331        targets: census.targets.into_owned(),
332    })
333}
334
335#[cfg(test)]
336mod tests;