Skip to main content

structured_ltv/material/
incorporation.rs

1//! Actual X.509 validation inputs incorporated into the original signature.
2
3use super::{ArtifactRef, ArtifactUses, Index, IndexError, Occurrence};
4use crate::source::{Query, Snapshot};
5use crate::timestamp::TimestampScope;
6use structured_asn1::memory::{OwnedVec, Work};
7
8/// One actual validation input and an exact original incorporated copy, if any.
9/// An acquired/caller-supplied value alone is not an incorporated occurrence.
10pub struct IncorporatedArtifact<'certificate, 'document, N, D, G, P> {
11    artifact: ArtifactRef<'certificate, 'document>,
12    occurrence: Option<Occurrence<N, D, G, P>>,
13}
14
15impl<'certificate, 'document, N: Copy, D: Copy, G: Copy, P: Copy>
16    IncorporatedArtifact<'certificate, 'document, N, D, G, P>
17{
18    /// Original actual checked-path or contributing status input.
19    #[must_use]
20    pub fn artifact(&self) -> ArtifactRef<'certificate, 'document> {
21        self.artifact
22    }
23    /// First exact incorporated copy; absence never implies usable status.
24    #[must_use]
25    pub fn occurrence(&self) -> Option<Occurrence<N, D, G, P>> {
26        self.occurrence
27    }
28}
29
30/// Complete X.509 input presence for whole-signature LT requirements. This
31/// preserves missing artifacts rather than accepting a caller-written subset.
32/// Placement, format-specific credentials, status usability, historical trust,
33/// chronology and archive coverage are SEPARATE obligations, not inferred here.
34pub struct Incorporation<'certificate, 'document, N, D, G, P> {
35    snapshot: Snapshot<D, G>,
36    signature: N,
37    entries: OwnedVec<IncorporatedArtifact<'certificate, 'document, N, D, G, P>>,
38    all_present: bool,
39}
40
41impl<'certificate, 'document, N: Copy, D: Copy, G: Copy, P: Copy>
42    Incorporation<'certificate, 'document, N, D, G, P>
43{
44    /// Actual immutable source under which every copy was located.
45    #[must_use]
46    pub fn snapshot(&self) -> Snapshot<D, G> {
47        self.snapshot
48    }
49    /// Original root signature whose complete participant set was used.
50    #[must_use]
51    pub fn signature(&self) -> N {
52        self.signature
53    }
54    /// Every actual artifact once, preserving first-encounter order.
55    #[must_use]
56    pub fn entries(&self) -> &[IncorporatedArtifact<'certificate, 'document, N, D, G, P>] {
57        &self.entries
58    }
59    /// Only incorporated presence, not allowed placement, Good status or grade.
60    #[must_use]
61    pub fn all_present(&self) -> bool {
62        self.all_present
63    }
64}
65
66/// Incomplete scope and original source/resource errors are not missing values.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum IncorporationError<N> {
69    /// A preceding-archive subset cannot establish whole-signature presence.
70    Scope,
71    /// Current original source/index/generation/admission refused.
72    Index(IndexError<N>),
73}
74
75/// Locate every actual X.509 validation input, including independently retained
76/// anchors and reached CRL/delta/OCSP authentication paths, through the SAME
77/// exact-artifact index used for archive coverage and TSA placement.
78/// EN319132-1 §6.3(cc) permits missing validation data in `AnyValidationData`;
79/// §§5.4.2–5.4.6 and6.3(x) additionally constrain particular container roles.
80/// This necessary presence check does not replace those placement checks or
81/// assert that Unknown/stale/revoked input is Good. No parse/crypto is repeated.
82/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
83pub fn incorporation<'certificate, 'document, N, D, G, P, O, W, Q, I>(
84    uses: &ArtifactUses<'_, '_, '_, 'certificate, 'document, N, D, G, I>,
85    index: &Index<'_, N, D, G, P, O, W, I>,
86    query: &Q,
87) -> Result<Incorporation<'certificate, 'document, N, D, G, P>, IncorporationError<N>>
88where
89    N: Copy + Eq,
90    D: Copy + Eq,
91    G: Copy + Eq,
92    P: Copy,
93    W: Work,
94    Q: Query<Id = N, Document = D, Generation = G>,
95{
96    let required = uses.inputs().requirements();
97    let TimestampScope::All(location) = required.scope() else {
98        return Err(IncorporationError::Scope);
99    };
100    if location.snapshot != query.snapshot() {
101        return Err(IncorporationError::Index(IndexError::Foreign {
102            signature: required.signature(),
103        }));
104    }
105    index
106        .require_signature(required.signature(), query)
107        .map_err(IncorporationError::Index)?;
108    let resource = |cause| {
109        IncorporationError::Index(IndexError::Resource {
110            signature: required.signature(),
111            cause,
112        })
113    };
114    let mut entries = query.execution().vector();
115    entries
116        .try_reserve_exact(uses.entries().len())
117        .map_err(resource)?;
118    let mut all_present = true;
119    for ordinal in 0..uses.entries().len() {
120        query
121            .admit(required.signature(), 2)
122            .map_err(|cause| IncorporationError::Index(IndexError::Scope(cause)))?;
123        let artifact = uses.entries()[ordinal].artifact();
124        let mut copies = index
125            .artifact(artifact, query)
126            .map_err(IncorporationError::Index)?;
127        let occurrence = copies
128            .next()
129            .transpose()
130            .map_err(IncorporationError::Index)?;
131        if occurrence.is_none() {
132            all_present = false;
133        }
134        entries
135            .try_push(IncorporatedArtifact {
136                artifact,
137                occurrence,
138            })
139            .map_err(resource)?;
140    }
141    index
142        .require_signature(required.signature(), query)
143        .map_err(IncorporationError::Index)?;
144    Ok(Incorporation {
145        snapshot: location.snapshot,
146        signature: required.signature(),
147        entries: entries.into_owned(),
148        all_present,
149    })
150}