Skip to main content

structured_xades/material/
index.rs

1//! The common artifact index over this format's material pools.
2
3use super::{Opaque, Placement, Value, View};
4use crate::timestamp::CarriedMaterial;
5use crate::xml::{Access, Xml};
6use crate::{ISSUER, Xades};
7use structured_asn1::memory::Work;
8use structured_ltv::material::{
9    self as ltv, ArtifactRef, ArtifactSource, CarriedOrigin, IndexError,
10};
11
12/// Exact artifact index of one signature's `XAdES` material, keyed by original
13/// DER and carrying each occurrence's container placement.
14pub type Index<'source, N, D, G, W> =
15    ltv::Index<'source, N, D, G, Placement<N>, Opaque<N, D, G>, W, Xades>;
16/// One original occurrence and its `XAdES` container placement.
17pub type Occurrence<N, D, G> = ltv::Occurrence<N, D, G, Placement<N>>;
18/// Archive availability of one artifact, with its `XAdES` placement.
19pub type Archived<N, D, G> = ltv::Archived<N, D, G, Placement<N>>;
20
21/// The index of this XML owner's material, or the refusal to build it.
22pub type IndexResult<'source, X, W> = Result<
23    Index<'source, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, W>,
24    IndexError<<X as Xml>::Id>,
25>;
26
27/// Builds the index from the already parsed/frozen set without decoding values
28/// again. All source and embedded certificate metadata, exact hashing/equality,
29/// hidden table probes, entries and duplicate links are funded before work.
30/// Refusal drops the complete partial index, preserving original sources.
31///
32/// Only this crate adds occurrences, so a caller cannot plant material the
33/// document does not carry:
34///
35/// ```compile_fail
36/// use structured_asn1::memory::Memory;
37/// use structured_ltv::material::{ArtifactRef, ArtifactSource};
38/// use structured_ltv::source::{Location, Query};
39/// use structured_xades::material::{Index, Occurrence, Placement};
40/// fn plant<'s, Q: Query<Id = usize, Document = u8, Generation = u8, Work = Memory>>(
41///     index: &mut Index<'s, usize, u8, u8, Memory>,
42///     value: &structured_pkix::Certificate<'s>,
43///     at: Location<usize, u8, u8>,
44///     query: &Q,
45/// ) {
46///     let occurrence = Occurrence::new(ArtifactSource::Material(0), at, Placement::General(0));
47///     let _ = index.insert(ArtifactRef::Certificate(value), occurrence, query);
48/// }
49/// ```
50pub fn index<'source, X: Xml, W: Work>(
51    view: &View<'source, X::Id, X::Document, X::Generation>,
52    access: &Access<'_, X, W>,
53) -> IndexResult<'source, X, W> {
54    let signature = view.signature;
55    if view.snapshot != access.snapshot() {
56        return Err(IndexError::Foreign { signature });
57    }
58    let mut index = Index::new(ISSUER, signature, view.opaque(), access)?;
59    let mut next_basic = 0;
60    for ordinal in 0..view.values.len() {
61        index.admit(access)?;
62        let value = &view.values[ordinal];
63        let placement = view.sources[ordinal].placement();
64        let artifact = match &value.value {
65            Value::Certificate(value) => ArtifactRef::Certificate(value),
66            Value::Crl(value) => ArtifactRef::Crl(value),
67            Value::Ocsp(_) => {
68                if let Some(basic) = view.basic.get(next_basic)
69                    && basic.source_index() == ordinal
70                {
71                    index.insert(
72                        ISSUER,
73                        ArtifactRef::Ocsp(basic.response()),
74                        Occurrence::new(
75                            ArtifactSource::Material(basic.source_index()),
76                            basic.location(),
77                            placement,
78                        ),
79                        access,
80                    )?;
81                    next_basic += 1;
82                }
83                continue;
84            }
85        };
86        index.insert(
87            ISSUER,
88            artifact,
89            Occurrence::new(ArtifactSource::Material(ordinal), value.location, placement),
90            access,
91        )?;
92    }
93    index.admit(access)?;
94    Ok(index)
95}
96
97/// Builds one exact-artifact index from both already parsed original source
98/// sets. XML-pool occurrences stay first; token-pool occurrences retain their
99/// explicit pool/ordinal and actual placement. All growth belongs to this
100/// current execution. Any late refusal drops the complete partial index
101/// instead of returning a mutable prefix.
102pub fn index_with_tokens<'source, X: Xml, W: Work>(
103    view: &View<'source, X::Id, X::Document, X::Generation>,
104    material: &CarriedMaterial<'source, X::Id, X::Document, X::Generation>,
105    access: &Access<'_, X, W>,
106) -> IndexResult<'source, X, W> {
107    if material.view.snapshot() != view.snapshot {
108        return Err(IndexError::Foreign {
109            signature: view.signature,
110        });
111    }
112    if !access
113        .equal_id(material.view.signature(), view.signature)
114        .map_err(|cause| IndexError::Resource {
115            signature: view.signature,
116            cause,
117        })?
118    {
119        return Err(IndexError::Foreign {
120            signature: view.signature,
121        });
122    }
123    let mut index = index(view, access)?;
124    // The two immutable source sets were bound above; each insertion still
125    // admits under the current source snapshot.
126    index.admit(access)?;
127    for token in 0..material.view.readings().len() {
128        index.admit(access)?;
129        let source = &material.view.sources()[token];
130        let placement = Placement::TimestampToken {
131            property: source.property(),
132            token: source.location().node,
133            role: source.role(),
134        };
135        let certificates = material.view.readings()[token]
136            .token()
137            .message()
138            .certificates();
139        for (certificate, value) in certificates.iter().enumerate() {
140            index.admit(access)?;
141            index.insert(
142                ISSUER,
143                ArtifactRef::Certificate(value),
144                Occurrence::new(
145                    ArtifactSource::TokenCertificate { token, certificate },
146                    source.location(),
147                    placement,
148                ),
149                access,
150            )?;
151        }
152    }
153    for value in material.crls() {
154        index.admit(access)?;
155        index.insert(
156            ISSUER,
157            ArtifactRef::Crl(&value.value),
158            carried(material, value.origin),
159            access,
160        )?;
161    }
162    for value in material.basic_responses() {
163        index.admit(access)?;
164        index.insert(
165            ISSUER,
166            ArtifactRef::Ocsp(&value.value),
167            carried(material, value.origin),
168            access,
169        )?;
170    }
171    index.admit(access)?;
172    Ok(index)
173}
174
175fn carried<N: Copy, D: Copy, G: Copy>(
176    material: &CarriedMaterial<'_, N, D, G>,
177    origin: CarriedOrigin,
178) -> Occurrence<N, D, G> {
179    let source = &material.view.sources()[origin.token];
180    Occurrence::new(
181        ArtifactSource::TokenRevocation(origin),
182        source.location(),
183        Placement::TimestampToken {
184            property: source.property(),
185            token: source.location().node,
186            role: source.role(),
187        },
188    )
189}