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