Skip to main content

structured_xades/timestamp/
material.rs

1//! Carried CMS revocation material, separate from XML material-pool ordinals.
2
3use super::TokenView;
4use crate::binary::Location;
5use structured_asn1::{
6    Element, Oid, Reader, Tag,
7    memory::{MemoryError, OwnedVec, Work},
8    universal,
9};
10use structured_cms::signed::OtherRevocationInfo;
11use structured_ltv::material::CarriedOrigin;
12use structured_pkix::{
13    crl::CertificateList,
14    ocsp::{BasicResponse, ResponseBytes},
15};
16
17/// One original carried artifact; availability is not authorization or status.
18pub struct Carried<T> {
19    /// Exact original token and choice, not a fingerprint or XML material index.
20    pub origin: CarriedOrigin,
21    /// Shared parsed original artifact, without normalization or copying.
22    pub value: T,
23}
24
25/// Unsupported CMS revocation format, retained without claiming usable status.
26pub struct OtherCarried<'source> {
27    /// Original format identifier.
28    pub kind: Oid<'source>,
29    /// Whole original ANY element and its decoded-source location.
30    pub value: Element<'source>,
31}
32
33/// Parsed known carried material, borrowing the frozen original token sources.
34/// Embedded certificates remain available through each shared `BasicResponse`;
35/// token certificates remain in the already parsed `SignedData` catalogue.
36pub struct CarriedMaterial<'source, N, D, G> {
37    pub(crate) view: TokenView<'source, N, D, G>,
38    crls: OwnedVec<Carried<CertificateList<'source>>>,
39    responses: OwnedVec<Carried<ResponseBytes<'source>>>,
40    basic: OwnedVec<Carried<BasicResponse<'source>>>,
41    other: OwnedVec<Carried<OtherCarried<'source>>>,
42}
43
44impl<'source, N, D, G> CarriedMaterial<'source, N, D, G> {
45    /// Original complete and delta CRLs; pairing/authentication remain separate.
46    #[must_use]
47    pub fn crls(&self) -> &[Carried<CertificateList<'source>>] {
48        &self.crls
49    }
50    /// Successful whole OCSP envelopes, including unsupported inner syntaxes.
51    #[must_use]
52    pub fn responses(&self) -> &[Carried<ResponseBytes<'source>>] {
53        &self.responses
54    }
55    /// Known basic responses parsed once with their original embedded certificates.
56    #[must_use]
57    pub fn basic_responses(&self) -> &[Carried<BasicResponse<'source>>] {
58        &self.basic
59    }
60    /// Original unsupported CMS formats, never counted as authenticated evidence.
61    #[must_use]
62    pub fn other(&self) -> &[Carried<OtherCarried<'source>>] {
63        &self.other
64    }
65}
66
67/// Original typed refusal; XML enclosure is physical identity, while inner
68/// offsets refer to that token's retained decoded source, not physical XML.
69#[derive(Debug)]
70pub enum CarriedCause {
71    /// Current operation's metadata traversal was refused.
72    Encoding(structured_asn1::Error),
73    /// Common CMS structural/format processing refused.
74    Cms(structured_cms::Error),
75    /// Shared CRL/basic-response syntax processing refused.
76    Pkix(structured_pkix::Error),
77    /// New catalogue metadata/capacity was refused before allocation.
78    Resource(MemoryError),
79}
80
81/// Failure identifies the exact source pool and original XML enclosure even
82/// after the scoped source buffers have been dropped.
83#[derive(Debug)]
84pub struct CarriedError<N, D, G> {
85    /// Original encoded token XML enclosure and source generation.
86    pub location: Location<N, D, G>,
87    /// Actual token/choice; the choice may be the next not-yet-admitted item.
88    pub origin: CarriedOrigin,
89    /// Original cause, not converted to absent material or an untrusted status.
90    pub cause: CarriedCause,
91}
92
93impl<'source, N: Copy, D: Copy, G: Copy> TokenView<'source, N, D, G> {
94    /// Decodes all known revocation choices before returning any catalogue.
95    /// A partial result is dropped on failure; original source buffers remain
96    /// intact. Current operation and original source ceilings both govern every
97    /// shared decoder. No acquisition, crypto, actor selection or B-level claim.
98    pub fn material_in<'query>(
99        &self,
100        operation: &Reader<'query>,
101    ) -> Result<CarriedMaterial<'query, N, D, G>, CarriedError<N, D, G>>
102    where
103        'source: 'query,
104    {
105        let execution = operation.execution();
106        let mut crls = execution.vector();
107        let mut responses = execution.vector();
108        let mut basic = execution.vector();
109        let mut other = execution.vector();
110        if let Some(first) = self.sources().first() {
111            operation
112                .charge(self.sources().len() as u64)
113                .map_err(|cause| CarriedError {
114                    location: first.location(),
115                    origin: CarriedOrigin {
116                        token: 0,
117                        choice: 0,
118                    },
119                    cause: CarriedCause::Encoding(cause),
120                })?;
121        }
122        for token in 0..self.sources().len() {
123            let source = &self.sources()[token];
124            let mut choice = 0;
125            loop {
126                let origin = CarriedOrigin { token, choice };
127                let error = |cause| CarriedError {
128                    location: source.location(),
129                    origin,
130                    cause,
131                };
132                let message = self.readings()[token].token().message();
133                let Some(reader) = message
134                    .revocation_reader_in(operation, choice)
135                    .map_err(|cause| error(CarriedCause::Cms(cause)))?
136                else {
137                    break;
138                };
139                let entry = message.revocations()[choice];
140                if entry.tag() == Tag::constructed(universal::SEQUENCE) {
141                    crls.try_reserve(1)
142                        .map_err(|cause| error(CarriedCause::Resource(cause)))?;
143                    let value = CertificateList::read_element(&reader, &entry)
144                        .map_err(|cause| error(CarriedCause::Pkix(cause)))?;
145                    crls.try_push(Carried { origin, value })
146                        .map_err(|cause| error(CarriedCause::Resource(cause)))?;
147                } else {
148                    let format = OtherRevocationInfo::read(&reader, &entry)
149                        .map_err(|cause| error(CarriedCause::Cms(cause)))?;
150                    let value = OtherCarried {
151                        kind: format.kind(),
152                        value: format.value(),
153                    };
154                    if let Some(response) = format
155                        .ocsp()
156                        .map_err(|cause| error(CarriedCause::Cms(cause)))?
157                    {
158                        responses
159                            .try_reserve(1)
160                            .map_err(|cause| error(CarriedCause::Resource(cause)))?;
161                        if let Some(value) = response
162                            .basic_in(operation)
163                            .map_err(|cause| error(CarriedCause::Pkix(cause)))?
164                        {
165                            basic
166                                .try_push(Carried { origin, value })
167                                .map_err(|cause| error(CarriedCause::Resource(cause)))?;
168                        }
169                        responses
170                            .try_push(Carried {
171                                origin,
172                                value: response,
173                            })
174                            .map_err(|cause| error(CarriedCause::Resource(cause)))?;
175                    } else {
176                        other
177                            .try_push(Carried { origin, value })
178                            .map_err(|cause| error(CarriedCause::Resource(cause)))?;
179                    }
180                }
181                choice += 1;
182            }
183        }
184        Ok(CarriedMaterial {
185            view: self.reborrow(),
186            crls: crls.into_owned(),
187            responses: responses.into_owned(),
188            basic: basic.into_owned(),
189            other: other.into_owned(),
190        })
191    }
192}