1use super::{CheckedPath, exact};
4use structured_asn1::memory::{MemoryError, Work};
5use structured_pkix::{
6 crl::{AbsenceEvidence, Authenticated, EvidenceSource},
7 path::WorkingKey,
8 temporal::{LeapTable, Reading, Uncertainty},
9};
10
11#[derive(Clone, Copy)]
13pub enum CrlAuthority<'view, 'document> {
14 Anchor,
17 Path(&'view CheckedPath<'document>),
19}
20
21#[derive(Clone, Copy)]
24pub enum CrlAuthorities<'view, 'document> {
25 Complete(CrlAuthority<'view, 'document>),
27 Delta {
29 base: CrlAuthority<'view, 'document>,
31 delta: CrlAuthority<'view, 'document>,
33 },
34}
35
36pub struct CrlCoverage<'view, 'certificate, 'document> {
41 path: &'view CheckedPath<'document>,
42 member: usize,
43 coverage: &'view structured_pkix::crl::Coverage<'certificate, 'document>,
44 authorities: &'view [CrlAuthorities<'view, 'document>],
45}
46
47impl<'view, 'certificate, 'document> CrlCoverage<'view, 'certificate, 'document> {
48 #[must_use]
50 pub fn path(&self) -> &'view CheckedPath<'document> {
51 self.path
52 }
53 #[must_use]
55 pub fn member(&self) -> usize {
56 self.member
57 }
58 #[must_use]
60 pub fn coverage(&self) -> &'view structured_pkix::crl::Coverage<'certificate, 'document> {
61 self.coverage
62 }
63 #[must_use]
65 pub fn authorities(&self) -> &'view [CrlAuthorities<'view, 'document>] {
66 self.authorities
67 }
68}
69
70pub enum CrlCoverageBinding<'view, 'certificate, 'document> {
72 Bound(CrlCoverage<'view, 'certificate, 'document>),
74 Rejected {
76 source: usize,
78 cause: CrlBindingFailure,
80 },
81}
82
83#[derive(Debug)]
85pub enum CrlCoverageError {
86 AuthorityCount,
88 Source {
90 source: usize,
92 cause: CrlBindingError,
94 },
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum CrlBindingFailure {
100 Target,
102 Anchor,
104 Signer,
106 EffectiveKey,
108 Interval,
110}
111
112#[derive(Debug)]
114pub enum CrlBindingError {
115 MemberIndex {
117 index: usize,
119 },
120 SourceKind,
122 Resource(MemoryError),
124 Name(structured_pkix::Error),
126 Time(Uncertainty),
128}
129
130impl From<super::authority::Error> for CrlBindingError {
131 fn from(cause: super::authority::Error) -> Self {
132 match cause {
133 super::authority::Error::Resource(cause) => Self::Resource(cause),
134 super::authority::Error::Name(cause) => Self::Name(cause),
135 super::authority::Error::Time(cause) => Self::Time(cause),
136 }
137 }
138}
139
140impl From<super::authority::Failure> for CrlBindingFailure {
141 fn from(cause: super::authority::Failure) -> Self {
142 match cause {
143 super::authority::Failure::Anchor => Self::Anchor,
144 super::authority::Failure::Signer => Self::Signer,
145 super::authority::Failure::EffectiveKey => Self::EffectiveKey,
146 super::authority::Failure::Interval => Self::Interval,
147 }
148 }
149}
150
151pub struct CrlTarget<'view, 'certificate, 'document> {
154 path: &'view CheckedPath<'document>,
155 member: usize,
156 evidence: &'view AbsenceEvidence<'certificate, 'document>,
157 authorities: CrlAuthorities<'view, 'document>,
158}
159
160impl<'view, 'certificate, 'document> CrlTarget<'view, 'certificate, 'document> {
161 #[must_use]
163 pub fn path(&self) -> &'view CheckedPath<'document> {
164 self.path
165 }
166 #[must_use]
168 pub fn member(&self) -> usize {
169 self.member
170 }
171 #[must_use]
173 pub fn evidence(&self) -> &'view AbsenceEvidence<'certificate, 'document> {
174 self.evidence
175 }
176 #[must_use]
178 pub fn authorities(&self) -> &CrlAuthorities<'view, 'document> {
179 &self.authorities
180 }
181}
182
183pub enum CrlBinding<'view, 'certificate, 'document> {
185 Bound(CrlTarget<'view, 'certificate, 'document>),
187 Rejected(CrlBindingFailure),
189}
190
191#[derive(Clone, Copy)]
192pub(super) enum CrlSource<'view, 'certificate, 'document> {
193 Absence(&'view AbsenceEvidence<'certificate, 'document>),
194 Reading(super::CrlReading<'view, 'certificate, 'document>),
195}
196
197impl<'certificate, 'document> CrlSource<'_, 'certificate, 'document> {
198 fn target(self) -> &'certificate structured_pkix::Certificate<'document> {
199 match self {
200 Self::Absence(value) => value.target(),
201 Self::Reading(value) => value.target(),
202 }
203 }
204 fn at(self) -> Reading {
205 match self {
206 Self::Absence(value) => value.evaluated_at(),
207 Self::Reading(value) => value.evaluated_at(),
208 }
209 }
210 fn source(self) -> EvidenceSource<'certificate, 'document> {
211 match self {
212 Self::Absence(value) => value.source(),
213 Self::Reading(value) => value.source(),
214 }
215 }
216}
217
218impl<'document> CheckedPath<'document> {
219 pub(super) fn bind_crl_source<W: Work>(
220 &self,
221 member: usize,
222 facts: CrlSource<'_, '_, 'document>,
223 authorities: CrlAuthorities<'_, 'document>,
224 utc: &LeapTable<'_>,
225 execution: &W,
226 ) -> Result<Option<CrlBindingFailure>, CrlBindingError> {
227 execution
228 .charge_work(1)
229 .map_err(CrlBindingError::Resource)?;
230 let Some(expected) = self.members.get(member) else {
231 return Err(CrlBindingError::MemberIndex { index: member });
232 };
233 if !exact(
234 expected.certificate().as_bytes(),
235 facts.target().as_bytes(),
236 execution,
237 )
238 .map_err(CrlBindingError::Resource)?
239 {
240 return Ok(Some(CrlBindingFailure::Target));
241 }
242 match (facts.source(), authorities) {
246 (EvidenceSource::Complete(source), CrlAuthorities::Complete(authority)) => {
247 self.crl_authority(source, authority, facts.at(), utc, execution)
248 }
249 (
250 EvidenceSource::Delta { base, delta },
251 CrlAuthorities::Delta {
252 base: first,
253 delta: second,
254 },
255 ) => {
256 if let Some(failure) =
257 self.crl_authority(base, first, facts.at(), utc, execution)?
258 {
259 return Ok(Some(failure));
260 }
261 self.crl_authority(delta, second, facts.at(), utc, execution)
262 }
263 _ => Err(CrlBindingError::SourceKind),
264 }
265 }
266
267 pub fn bind_crl_coverage<'view, 'certificate, W: Work>(
274 &'view self,
275 member: usize,
276 coverage: &'view structured_pkix::crl::Coverage<'certificate, 'document>,
277 authorities: &'view [CrlAuthorities<'view, 'document>],
278 utc: &LeapTable<'_>,
279 execution: &W,
280 ) -> Result<CrlCoverageBinding<'view, 'certificate, 'document>, CrlCoverageError> {
281 execution
282 .charge_work(1)
283 .map_err(|cause| CrlCoverageError::Source {
284 source: 0,
285 cause: CrlBindingError::Resource(cause),
286 })?;
287 if authorities.len() != coverage.sources().len() {
288 return Err(CrlCoverageError::AuthorityCount);
289 }
290 for (source, authority) in authorities.iter().enumerate() {
291 execution
292 .charge_work(1)
293 .map_err(|cause| CrlCoverageError::Source {
294 source,
295 cause: CrlBindingError::Resource(cause),
296 })?;
297 match self
298 .bind_crl(
299 member,
300 &coverage.sources()[source],
301 *authority,
302 utc,
303 execution,
304 )
305 .map_err(|cause| CrlCoverageError::Source { source, cause })?
306 {
307 CrlBinding::Bound(_) => {}
308 CrlBinding::Rejected(cause) => {
309 return Ok(CrlCoverageBinding::Rejected { source, cause });
310 }
311 }
312 }
313 Ok(CrlCoverageBinding::Bound(CrlCoverage {
314 path: self,
315 member,
316 coverage,
317 authorities,
318 }))
319 }
320
321 pub fn bind_crl<'view, 'certificate, W: Work>(
332 &'view self,
333 member: usize,
334 evidence: &'view AbsenceEvidence<'certificate, 'document>,
335 authorities: CrlAuthorities<'view, 'document>,
336 utc: &LeapTable<'_>,
337 execution: &W,
338 ) -> Result<CrlBinding<'view, 'certificate, 'document>, CrlBindingError> {
339 if let Some(failure) = self.bind_crl_source(
340 member,
341 CrlSource::Absence(evidence),
342 authorities,
343 utc,
344 execution,
345 )? {
346 return Ok(CrlBinding::Rejected(failure));
347 }
348 Ok(CrlBinding::Bound(CrlTarget {
349 path: self,
350 member,
351 evidence,
352 authorities,
353 }))
354 }
355
356 fn crl_authority<W: Work>(
357 &self,
358 source: Authenticated<'_, 'document>,
359 authority: CrlAuthority<'_, 'document>,
360 at: Reading,
361 utc: &LeapTable<'_>,
362 execution: &W,
363 ) -> Result<Option<CrlBindingFailure>, CrlBindingError> {
364 let resource = CrlBindingError::Resource;
365 execution.charge_work(1).map_err(resource)?;
366 let expected = match authority {
367 CrlAuthority::Anchor => {
368 if !self
369 .anchor
370 .name()
371 .matches(source.issuer().tbs().subject(), execution)
372 .map_err(CrlBindingError::Name)?
373 {
374 return Ok(Some(CrlBindingFailure::Signer));
375 }
376 WorkingKey::standing(self.anchor.key())
377 }
378 CrlAuthority::Path(path) => {
379 return Ok(self
380 .path_authority(path, source.issuer(), source.key(), at, utc, execution)?
381 .map(Into::into));
382 }
383 };
384 if !exact(
385 expected.key().as_bytes(),
386 source.key().key().as_bytes(),
387 execution,
388 )
389 .map_err(resource)?
390 {
391 return Ok(Some(CrlBindingFailure::Signer));
392 }
393 let same = match (expected.parameters(), source.key().parameters()) {
394 (None, None) => true,
395 (Some(left), Some(right)) => exact(left, right, execution).map_err(resource)?,
396 _ => false,
397 };
398 if same {
399 return Ok(None);
400 }
401 Ok(Some(CrlBindingFailure::EffectiveKey))
402 }
403}