Skip to main content

structured_asic/
content.rs

1//! A complete original member owns its decoded CMS or evidence-record source.
2
3use crate::{
4    error::Error,
5    zip::{Member, MemberId, ReadFailure},
6};
7use structured_asn1::{
8    Reader, Span,
9    memory::{OwnedVec, Work},
10    range::RangeSource,
11};
12use structured_cms::{ContentInfo, SignedData, evidence::EvidenceRecord, timestamp::Token};
13
14/// A complete carried extent cannot be interpreted as the selected envelope.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Syntax {
17    /// No element was present.
18    Empty,
19    /// Bytes follow the one complete evidence record.
20    Trailing,
21    /// `ContentInfo` names an alternative content type.
22    ContentType,
23}
24
25/// Physical member provenance and decoded logical diagnostics stay distinct.
26#[derive(Debug)]
27pub enum CarriedError<E, D, G> {
28    /// Original source/current query refused before the carried operation.
29    Archive(Error<E>),
30    /// Original ASN.1 diagnostic in decoded-member coordinates.
31    Encoding {
32        /// Original member, independent of a temporary parsed view.
33        member: MemberId<D, G>,
34        /// Honest physical enclosure, not an invented inner physical offset.
35        physical: Span,
36        /// Unchanged original logical diagnostic.
37        cause: structured_asn1::Error,
38    },
39    /// Original CMS or RFC4998 diagnostic in decoded-member coordinates.
40    Cms {
41        /// Original member identity.
42        member: MemberId<D, G>,
43        /// Actual physical enclosure of the encoded member.
44        physical: Span,
45        /// Unchanged common error.
46        cause: structured_cms::Error,
47    },
48    /// A completed negative carried-envelope fact.
49    Syntax {
50        /// Original member identity.
51        member: MemberId<D, G>,
52        /// Actual physical enclosure.
53        physical: Span,
54        /// Envelope-level negative fact.
55        kind: Syntax,
56    },
57}
58
59/// Independent storage for one complete original decoded member. Its ASN.1
60/// cursor always inherits the archive's producing session and current query;
61/// neither moving storage nor reopening a cursor resets cumulative work.
62pub struct Content<'v, 's, S: RangeSource> {
63    member: Member<'v, 's, S>,
64    bytes: OwnedVec<u8>,
65}
66
67impl<'v, 's, S: RangeSource> Member<'v, 's, S> {
68    /// Acquires exactly this original occurrence through complete EOF/CRC.
69    /// Signed data objects can instead use `source` without retention.
70    pub async fn content<W: Work>(
71        &self,
72        work: &W,
73    ) -> Result<Content<'v, 's, S>, ReadFailure<S::Error, S::Document, S::Generation>> {
74        let bytes = self.read_bytes(work).await?;
75        let member =
76            self.archive()
77                .original(self.id(), work)
78                .map_err(|cause| ReadFailure::Processing {
79                    member: self.id(),
80                    cause,
81                })?;
82        Ok(Content { member, bytes })
83    }
84}
85
86/// Original logical reading result, retaining the source's acquisition cause.
87pub type ResultOf<T, S> = Result<
88    T,
89    CarriedError<
90        <S as RangeSource>::Error,
91        <S as RangeSource>::Document,
92        <S as RangeSource>::Generation,
93    >,
94>;
95
96impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
97    pub(crate) fn into_parts(self) -> (Member<'v, 's, S>, OwnedVec<u8>) {
98        (self.member, self.bytes)
99    }
100    /// Exact original central-directory occurrence that supplied these bytes.
101    #[must_use]
102    pub fn member(&self) -> &Member<'v, 's, S> {
103        &self.member
104    }
105    /// Complete decoded bytes, without copying or treating them as physical offsets.
106    #[must_use]
107    pub fn bytes(&self) -> &[u8] {
108        &self.bytes
109    }
110    /// Physical original local record, including any descriptor.
111    ///
112    /// # Panics
113    /// Only if the private complete-content construction invariant is broken:
114    /// complete CRC-checked bytes always have a validated local enclosure.
115    #[must_use]
116    pub fn enclosure(&self) -> Span {
117        self.member
118            .local_span()
119            .expect("complete original content has a local enclosure")
120    }
121    fn cursor<'q, W: Work + 'q>(&'q self, work: &W) -> ResultOf<Reader<'q>, S> {
122        self.member
123            .archive()
124            .admit(work, self.enclosure())
125            .map_err(CarriedError::Archive)?;
126        self.member
127            .session()
128            .carried(&self.bytes, work)
129            .map_err(|cause| self.encoding(cause))
130    }
131    fn encoding(
132        &self,
133        cause: structured_asn1::Error,
134    ) -> CarriedError<S::Error, S::Document, S::Generation> {
135        CarriedError::Encoding {
136            member: self.member.id(),
137            physical: self.enclosure(),
138            cause,
139        }
140    }
141    fn cms(
142        &self,
143        cause: structured_cms::Error,
144    ) -> CarriedError<S::Error, S::Document, S::Generation> {
145        CarriedError::Cms {
146            member: self.member.id(),
147            physical: self.enclosure(),
148            cause,
149        }
150    }
151    fn syntax(&self, kind: Syntax) -> CarriedError<S::Error, S::Document, S::Generation> {
152        CarriedError::Syntax {
153            member: self.member.id(),
154            physical: self.enclosure(),
155            kind,
156        }
157    }
158    /// Inspects the original carried CMS without a replacement parser or copy.
159    pub fn read<'q, W: Work + 'q>(&'q self, work: &W) -> ResultOf<(SignedData<'q>, Reader<'q>), S> {
160        let mut root = self.cursor(work)?;
161        let (content, parent) = ContentInfo::read(&mut root).map_err(|cause| self.cms(cause))?;
162        parent
163            .execution()
164            .charge_work(content.kind().as_bytes().len() as u64)
165            .map_err(|cause| {
166                CarriedError::Archive(crate::error::resource(self.enclosure(), cause))
167            })?;
168        if content.kind() != structured_cms::oid::SIGNED_DATA {
169            return Err(self.syntax(Syntax::ContentType));
170        }
171        let message = SignedData::read_element(&parent, content.content())
172            .map_err(|cause| self.cms(cause))?;
173        self.require(&message, &parent, work)
174            .map_err(CarriedError::Archive)?;
175        Ok((message, parent))
176    }
177    /// Uses the existing complete RFC3161 envelope reader on this source.
178    pub fn read_timestamp<'q, W: Work + 'q>(
179        &'q self,
180        work: &W,
181    ) -> ResultOf<(Token<'q>, Reader<'q>), S> {
182        let mut reader = self.cursor(work)?;
183        let token = Token::read_from(&mut reader).map_err(|cause| self.cms(cause))?;
184        self.require(token.message(), &reader, work)
185            .map_err(CarriedError::Archive)?;
186        Ok((token, reader))
187    }
188    /// Uses the shared RFC4998 grammar for one entire original member.
189    pub fn read_evidence<'q, W: Work + 'q>(
190        &'q self,
191        work: &W,
192    ) -> ResultOf<(EvidenceRecord<'q>, Reader<'q>), S> {
193        let mut reader = self.cursor(work)?;
194        let element = reader
195            .next_element()
196            .map_err(|cause| self.encoding(cause))?
197            .ok_or_else(|| self.syntax(Syntax::Empty))?;
198        if !reader.is_empty() {
199            return Err(self.syntax(Syntax::Trailing));
200        }
201        let record = EvidenceRecord::read(&reader, &element).map_err(|cause| self.cms(cause))?;
202        Ok((record, reader))
203    }
204    pub(crate) fn require<W: Work>(
205        &self,
206        message: &SignedData<'_>,
207        parent: &Reader<'_>,
208        work: &W,
209    ) -> Result<(), Error<S::Error>> {
210        self.require_original(message.as_bytes(), message.span().start, parent, work)
211    }
212    pub(crate) fn require_original<W: Work>(
213        &self,
214        original: &[u8],
215        offset: u64,
216        parent: &Reader<'_>,
217        work: &W,
218    ) -> Result<(), Error<S::Error>> {
219        self.member.archive().admit(work, self.enclosure())?;
220        let at = usize::try_from(offset).map_err(|_| Error::Foreign {
221            range: self.enclosure(),
222        })?;
223        let end = at.checked_add(original.len()).ok_or(Error::Foreign {
224            range: self.enclosure(),
225        })?;
226        if !self.member.session().owns(parent)
227            || self
228                .bytes
229                .get(at..end)
230                .is_none_or(|bytes| !core::ptr::eq(bytes, original))
231        {
232            return Err(Error::Foreign {
233                range: self.enclosure(),
234            });
235        }
236        Ok(())
237    }
238}