Skip to main content

structured_asic/
cades.rs

1//! `CAdES` execution over the actual original `ASiC` payload, without replay.
2
3use crate::{
4    content::Content,
5    error::Error as ArchiveError,
6    payload::Payload,
7    zip::{Member, MemberId, ReadFailure},
8};
9use structured_asn1::{
10    Reader,
11    memory::{OwnedVec, Work},
12    range::RangeSource,
13};
14use structured_cms::{
15    SignedData,
16    prepare::ResolvedSigner,
17    provider::SignatureProvider,
18    verify::{CheckError, Checked, ContentMode, DocumentBatch, DocumentChecks, PassError},
19};
20use structured_pkix::{Certificate, algorithm::digest::AsnDigest};
21
22/// Original resolved requests, without inventing an independent dispatcher.
23pub struct Challenge<'a, P: SignatureProvider<'a>> {
24    /// Original carried `SignedData` model belonging to the selected Content.
25    pub message: &'a SignedData<'a>,
26    /// Actual producing/current source reader.
27    pub parent: &'a Reader<'a>,
28    /// The one selected signature/digest provider.
29    pub provider: &'a P,
30    /// Existing common resolver's requests, moved without state cloning.
31    pub requests: OwnedVec<ResolvedSigner<'a, P>>,
32}
33
34/// Format framing and common/provider failures retain their independent cause.
35#[derive(Debug)]
36pub enum Error<SE, PE, D, G> {
37    /// One original root `SignerInfo` has no prepared request. Complete-container
38    /// execution cannot silently replace it with the selected successful roots.
39    MissingSigner(structured_asn1::Span),
40    /// Original archive, generation or current/source-budget refusal.
41    Archive(ArchiveError<SE>),
42    /// Original payload framing/decode/CRC failure before or during acquisition.
43    Source(ReadFailure<SE, D, G>),
44    /// Common batch initialization, preserving callback/resource classification.
45    Prepare(CheckError<PE>),
46    /// Failed complete payload pass. No partial check receipt is returned.
47    Pass(PassError<ReadFailure<SE, D, G>, PE>),
48}
49
50/// Mathematical checks privately bound to the actual input member chosen by
51/// this driver. A separately checked arbitrary source cannot create this type.
52pub struct Checks<'a, 'v, 's, S: RangeSource> {
53    content: &'a Content<'v, 's, S>,
54    payload: MemberId<S::Document, S::Generation>,
55    inner: DocumentChecks<'a>,
56}
57
58impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
59    /// Validates both member/session identities and payload framing BEFORE
60    /// initializing provider states, then drives the existing CMS batch with
61    /// the sealed original member stream. Negative signer answers are retained.
62    pub async fn check_cades<'a, P: SignatureProvider<'a>>(
63        &'a self,
64        challenge: Challenge<'a, P>,
65        payload: &impl Payload<'v, 's, S>,
66    ) -> Result<Checks<'a, 'v, 's, S>, Error<S::Error, P::Error, S::Document, S::Generation>> {
67        let execution = challenge.parent.execution();
68        self.require(challenge.message, challenge.parent, &execution)
69            .map_err(Error::Archive)?;
70        // Validate the FULL original root census before starting any provider
71        // state or payload pass. Multiple actual key challenges for one root
72        // remain possible; they cannot fill a missing different root.
73        let refusal = |cause| Error::Archive(crate::error::resource(self.enclosure(), cause));
74        let mut selected = execution.map();
75        for request in &challenge.requests {
76            challenge
77                .message
78                .require_signer(request.signer(), "asic.signerInfos")
79                .map_err(|cause| Error::Prepare(CheckError::Processing(cause)))?;
80            selected
81                .try_insert(request.signer().element().index(), ())
82                .map_err(refusal)?;
83        }
84        for signer in challenge.message.signers() {
85            if selected
86                .get(&signer.element().index(), &execution)
87                .map_err(refusal)?
88                .is_none()
89            {
90                return Err(Error::MissingSigner(signer.span()));
91            }
92        }
93        drop(selected);
94        if !core::ptr::eq(self.member().archive(), payload.member().archive()) {
95            return Err(Error::Archive(ArchiveError::Foreign {
96                range: self.enclosure(),
97            }));
98        }
99        let mut source = payload.source(&execution).map_err(Error::Source)?;
100        let batch = DocumentBatch::prepare(
101            challenge.provider,
102            challenge.message,
103            challenge.requests,
104            ContentMode::Detached,
105            challenge.parent,
106        )
107        .map_err(Error::Prepare)?;
108        let checks = batch
109            .run_detached_async(&mut source)
110            .await
111            .map_err(Error::Pass)?;
112        self.member()
113            .archive()
114            .admit(&execution, self.enclosure())
115            .map_err(Error::Archive)?;
116        Ok(Checks {
117            content: self,
118            payload: payload.member().id(),
119            inner: checks,
120        })
121    }
122}
123
124/// Existing `CAdES` binding refusal, without treating processing as mismatch.
125#[derive(Debug)]
126pub enum BindError<SE, PE> {
127    /// Original archive generation/current-source admission.
128    Archive(ArchiveError<SE>),
129    /// Exact common SID/ESS/path/source binding refusal.
130    Cades(structured_cades::basic::BindError<PE>),
131}
132
133/// The actual existing `CAdES` binding, privately linked to the `ASiC` input pass.
134/// Exposing the inner immutable receipt does not permit replacing that receipt.
135pub struct Signer<'r, 'm, 'd, 'p, 'v, 's, S: RangeSource> {
136    member: Member<'v, 's, S>,
137    payload: MemberId<S::Document, S::Generation>,
138    inner: structured_cades::basic::SignerBinding<'r, 'm, 'd, 'p>,
139}
140
141impl<'a, 'v, 's, S: RangeSource> Checks<'a, 'v, 's, S> {
142    /// Every actually executed prepared signer answer, without copying.
143    #[must_use]
144    pub fn checks(&self) -> &DocumentChecks<'a> {
145        &self.inner
146    }
147    /// Exact original payload or manifest consumed by this batch.
148    #[must_use]
149    pub fn payload(&self) -> MemberId<S::Document, S::Generation> {
150        self.payload
151    }
152    /// Runs the ONE existing `CAdES` SID/key/ESS/path binder on a check that this
153    /// original member pass actually produced. No supplied successful boolean
154    /// or separately checked arbitrary payload can create the linked signer.
155    pub fn bind<'provider, 'r, 'm, 'd, 'p, P: AsnDigest<'provider>>(
156        &self,
157        prepared: &'r structured_cades::prepare::Preparation<'m, 'd>,
158        checked: &Checked<'_>,
159        certificate: &'p Certificate<'d>,
160        path: Option<&'p structured_ltv::pki::CheckedPath<'d>>,
161        provider: &'provider P,
162        parent: &Reader<'provider>,
163    ) -> Result<Signer<'r, 'm, 'd, 'p, 'v, 's, S>, BindError<S::Error, P::Error>>
164    where
165        'd: 'provider,
166    {
167        let execution = parent.execution();
168        self.content
169            .require(self.inner.message(), parent, &execution)
170            .map_err(BindError::Archive)?;
171        let inner = structured_cades::basic::bind(
172            prepared,
173            &self.inner,
174            checked,
175            certificate,
176            path,
177            provider,
178            parent,
179        )
180        .map_err(BindError::Cades)?;
181        let member = self
182            .content
183            .member()
184            .archive()
185            .original(self.content.member().id(), &execution)
186            .map_err(BindError::Archive)?;
187        Ok(Signer {
188            member,
189            payload: self.payload,
190            inner,
191        })
192    }
193}
194
195/// One linked `CAdES` requested-level answer. Container census, manifest object
196/// digests, other signatures and preservation continuity remain independent.
197pub struct Assessment<'a, 'm, 'd, E, D, G> {
198    signature: MemberId<D, G>,
199    payload: MemberId<D, G>,
200    inner: structured_cades::baseline::Assessment<'a, 'm, 'd, E>,
201}
202
203/// Wrong receipt and actual processing refusal remain separate from a negative level.
204#[derive(Debug)]
205pub enum LevelError<E> {
206    /// Original archive generation, source or current query refused.
207    Archive(ArchiveError<E>),
208    /// Evidence was built from a different actual original `CAdES` binding.
209    Foreign,
210    /// Existing complete `CAdES` stage composition refused.
211    Cades(structured_cades::baseline::LevelError),
212}
213
214/// Complete source-bound common level answer or its original processing refusal.
215pub type LevelResult<'a, 'm, 'd, E, S> = Result<
216    Assessment<'a, 'm, 'd, E, <S as RangeSource>::Document, <S as RangeSource>::Generation>,
217    LevelError<<S as RangeSource>::Error>,
218>;
219
220impl<'r, 'm, 'd, 'p, S: RangeSource> Signer<'r, 'm, 'd, 'p, '_, '_, S> {
221    /// Build ordinary shared Basic/Timed/LongTerm/Archives from this exact
222    /// immutable signer. No duplicate `CAdES` stage or crypto implementation exists.
223    #[must_use]
224    pub fn inner(&self) -> &structured_cades::basic::SignerBinding<'r, 'm, 'd, 'p> {
225        &self.inner
226    }
227    /// Composes the existing complete `CAdES` stages after requiring their ACTUAL
228    /// root binding, not merely an equal original signer or a requested level.
229    pub fn assess<'e, E>(
230        &'e self,
231        evidence: structured_cades::baseline::Evidence<'e, 'm, 'd, E>,
232        operation: &Reader<'_>,
233    ) -> LevelResult<'e, 'm, 'd, E, S> {
234        self.member
235            .archive()
236            .admit(&operation.execution(), self.member.central_span())
237            .map_err(LevelError::Archive)?;
238        let binding = match &evidence {
239            structured_cades::baseline::Evidence::Basic(basic) => basic.binding(),
240            structured_cades::baseline::Evidence::Timestamped(timed) => timed.basic().binding(),
241            structured_cades::baseline::Evidence::LongTerm(long)
242            | structured_cades::baseline::Evidence::Archival {
243                long_term: long, ..
244            } => long.timed().basic().binding(),
245        };
246        if !core::ptr::eq(
247            core::ptr::from_ref(binding).cast::<()>(),
248            core::ptr::from_ref(&self.inner).cast::<()>(),
249        ) {
250            return Err(LevelError::Foreign);
251        }
252        let inner =
253            structured_cades::baseline::assess(evidence, operation).map_err(LevelError::Cades)?;
254        Ok(Assessment {
255            signature: self.member.id(),
256            payload: self.payload,
257            inner,
258        })
259    }
260}
261
262impl<'a, 'm, 'd, E, D: Copy, G: Copy> Assessment<'a, 'm, 'd, E, D, G> {
263    /// Original signature member whose actual check supplied this evidence.
264    #[must_use]
265    pub fn signature(&self) -> MemberId<D, G> {
266        self.signature
267    }
268    /// Actual complete original input member, not a URI/name assertion.
269    #[must_use]
270    pub fn payload(&self) -> MemberId<D, G> {
271        self.payload
272    }
273    /// All original common `CAdES` stage answers, independent of container facts.
274    #[must_use]
275    pub fn cades(&self) -> &structured_cades::baseline::Assessment<'a, 'm, 'd, E> {
276        &self.inner
277    }
278}