1use super::aa_path::validated::{ScopedVerifier, VerificationError};
10use super::aa_path::{Issued, Permission};
11use super::authentication::Authenticated;
12use super::digest::BoundObject;
13use super::{Check, HolderMethod, Rejection, charge};
14use crate::attribute_certificate::controls::{self, AaControls};
15use crate::attribute_certificate::{proxy, target};
16use crate::{Error, Result};
17use structured_asn1::Oid;
18use structured_asn1::memory::{Execution, OwnedVec, Work};
19use structured_pkix::Certificate;
20use structured_pkix::extension::Recognizer;
21use structured_pkix::general_name::GeneralName;
22use structured_pkix::path::{self, EffectiveAnchor, Interval, Verify, Watch};
23use structured_pkix::temporal::{LeapTable, Reading, Uncertainty};
24
25mod status;
26pub use status::{Evidence, NoStatus, Status};
27
28#[derive(Debug)]
31pub struct HolderPath<'p, 'a> {
32 certificates: &'p [&'a Certificate<'a>],
33 facts: path::ValidationFacts<'a>,
34 anchor: EffectiveAnchor<'a>,
35 interval: Interval,
36}
37
38#[derive(Debug)]
40pub enum HolderValidation<T, P> {
41 Accepted(T),
43 Rejected(P),
45 Incomplete(path::Truncation),
47}
48
49pub type HolderResult<'p, 'a, V, R> = core::result::Result<
51 HolderValidation<HolderPath<'p, 'a>, path::Rejection<'a>>,
52 HolderError<V, R>,
53>;
54
55#[derive(Debug)]
57pub enum HolderError<V, R> {
58 Processing(Error),
60 Verification(V),
62 Recognition(R),
64}
65
66impl<'p, 'a> HolderPath<'p, 'a> {
67 #[must_use]
69 pub fn certificates(&self) -> &[&Certificate<'a>] {
70 self.certificates
71 }
72 #[must_use]
74 pub fn facts(&self) -> &path::ValidationFacts<'a> {
75 &self.facts
76 }
77 #[must_use]
79 pub fn anchor(&self) -> EffectiveAnchor<'a> {
80 self.anchor
81 }
82 #[must_use]
84 pub fn interval(&self) -> Interval {
85 self.interval
86 }
87 pub fn validate<V, T, R, W>(
91 certificates: &'p [&'a Certificate<'a>],
92 anchor: EffectiveAnchor<'a>,
93 context: &path::ValidationContext<'a, '_, V, T, R, W>,
94 ) -> HolderResult<'p, 'a, <V as Verify<W>>::Error, R::Error>
95 where
96 W: Work,
97 V: Verify<W>,
98 for<'op> V: Verify<Execution<'op>, Error = <V as Verify<W>>::Error>,
99 T: Watch,
100 R: Recognizer<W>,
101 {
102 let verifier = ScopedVerifier(context.verifier);
103 let scoped = path::ValidationContext {
104 execution: context.execution,
105 interval: context.interval,
106 policy: context.policy,
107 bounds: context.bounds,
108 verifier: &verifier,
109 watch: context.watch,
110 recognizer: context.recognizer,
111 };
112 let checked =
113 path::validate_effective(certificates, anchor, &scoped).map_err(
114 |error| match error {
115 path::BuildError::Processing(e) => HolderError::Processing(Error::pkix(e)),
116 path::BuildError::Recognition(e) => HolderError::Recognition(e),
117 path::BuildError::Verification(VerificationError::Callback(e)) => {
118 HolderError::Verification(e)
119 }
120 path::BuildError::Verification(VerificationError::Processing(e)) => {
121 HolderError::Processing(e)
122 }
123 },
124 )?;
125 Ok(match checked {
126 path::Validation::Accepted(facts) => HolderValidation::Accepted(Self {
127 certificates,
128 facts,
129 anchor,
130 interval: context.interval,
131 }),
132 path::Validation::Rejected(error) => HolderValidation::Rejected(error),
133 path::Validation::Incomplete(reason) => HolderValidation::Incomplete(reason),
134 })
135 }
136}
137
138#[derive(Debug)]
143pub enum Holder<'r, 'p, 'a, 'c> {
144 Certificate {
146 path: &'r HolderPath<'p, 'a>,
148 method: HolderMethod,
150 },
151 Object {
155 binding: &'r BoundObject<'r, 'c, 'a>,
157 path: Option<&'r HolderPath<'p, 'a>>,
159 },
160}
161
162#[derive(Debug, Clone, Copy)]
166pub struct Proxy<'n, 'a> {
167 pub sender: proxy::Sender<'a, 'n>,
169 pub intermediates: &'n [&'n [GeneralName<'a>]],
171}
172
173#[derive(Debug)]
176pub enum Failure {
177 AuthorityTrust,
179 Interval,
181 Time(Uncertainty),
183 Holder(Rejection),
185 HolderObject,
187 HolderPath,
189 Target(target::Decision),
191 ProxyContext,
193 Proxy(proxy::Decision),
195 Status(Status),
197}
198
199#[derive(Debug)]
201pub enum Outcome<T> {
202 Checked(T),
204 Rejected(Failure),
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub enum GroupRole {
211 Target,
213 Proxy(proxy::Subject),
215}
216
217pub struct Context<'r, 'p, 'a, 'c, 'op, S: Evidence, W: Work> {
221 pub holder: Holder<'r, 'p, 'a, 'c>,
223 pub recipient: &'r [GeneralName<'a>],
225 pub proxy: Option<Proxy<'r, 'a>>,
227 pub status: &'r S,
231 pub now: Reading,
233 pub utc: &'op LeapTable<'op>,
235 pub execution: &'op W,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub struct AttributeUse {
243 pub index: usize,
245 pub permission: Permission,
247}
248
249#[derive(Debug)]
251pub enum Authority<'r, 'p, 'a, 'c> {
252 Direct(&'r Authenticated<'r, 'c, 'a>),
254 Path(&'r Issued<'r, 'p, 'c, 'a>),
256}
257impl<'r, 'a, 'c> Authority<'r, '_, 'a, 'c> {
258 fn facts(&self) -> &'r Authenticated<'r, 'c, 'a> {
259 match self {
260 Self::Direct(facts) => facts,
261 Self::Path(issued) => issued.facts(),
262 }
263 }
264 fn allows<W: Work>(
265 &self,
266 kind: Oid<'_>,
267 controls: Option<&AaControls<'_>>,
268 work: &W,
269 ) -> Result<Permission> {
270 match self {
271 Self::Direct(facts) => {
272 charge(
273 &work.execution_for(facts.certificate().parser),
274 1,
275 facts.certificate().element().span().start,
276 )?;
277 if let Some(control) = controls
278 && !control.allows(kind, work)?
279 {
280 return Ok(Permission::Excluded {
281 certificate: 0,
282 source: control.element.span(),
283 });
284 }
285 Ok(Permission::Permitted)
286 }
287 Self::Path(issued) => issued.allows(kind, work),
288 }
289 }
290}
291
292#[derive(Debug)]
297pub struct Checked<'r, 'p, 'a, 'c, S> {
298 authority: Authority<'r, 'p, 'a, 'c>,
299 direct_controls: Option<AaControls<'a>>,
300 holder: Holder<'r, 'p, 'a, 'c>,
301 status: &'r S,
302 now: Reading,
303 target: Option<usize>,
304 proxy: Option<usize>,
305 attributes: OwnedVec<AttributeUse>,
306}
307impl<'r, 'p, 'a, 'c, S> Checked<'r, 'p, 'a, 'c, S> {
308 #[must_use]
311 pub fn certificate(&self) -> &'r crate::attribute_certificate::AttributeCertificate<'c> {
312 self.authority.facts().certificate()
313 }
314 #[must_use]
316 pub fn authority(&self) -> &Authority<'r, 'p, 'a, 'c> {
317 &self.authority
318 }
319 #[must_use]
321 pub fn holder(&self) -> &Holder<'r, 'p, 'a, 'c> {
322 &self.holder
323 }
324 #[must_use]
326 pub fn status(&self) -> &'r S {
327 self.status
328 }
329 #[must_use]
331 pub fn evaluated_at(&self) -> Reading {
332 self.now
333 }
334 #[must_use]
336 pub fn target(&self) -> Option<usize> {
337 self.target
338 }
339 #[must_use]
341 pub fn proxy(&self) -> Option<usize> {
342 self.proxy
343 }
344 #[must_use]
347 pub fn attributes(&self) -> &[AttributeUse] {
348 &self.attributes
349 }
350 pub fn allows<W: Work>(&self, kind: Oid<'_>, work: &W) -> Result<Permission> {
353 self.authority
354 .allows(kind, self.direct_controls.as_ref(), work)
355 }
356}
357
358#[derive(Debug)]
360pub enum UseError<E> {
361 Processing(Error),
363 Membership(target::CheckError<E>),
365}
366impl<E> From<Error> for UseError<E> {
367 fn from(error: Error) -> Self {
368 Self::Processing(error)
369 }
370}
371
372pub fn direct<'r, 'p, 'a, 'c, S: Evidence, W: Work, E>(
375 facts: &'r Authenticated<'r, 'c, 'a>,
376 trusted_aa: &Certificate<'_>,
377 context: Context<'r, 'p, 'a, 'c, '_, S, W>,
378 membership: impl FnMut(
379 GroupRole,
380 &GeneralName<'c>,
381 &Execution<'_>,
382 ) -> core::result::Result<target::Membership, E>,
383) -> core::result::Result<Outcome<Checked<'r, 'p, 'a, 'c, S>>, UseError<E>> {
384 let work = facts.authority().execution_in(context.execution);
385 charge(&work, 1, facts.authority().span().start)?;
386 if !core::ptr::eq(facts.authority(), trusted_aa) {
387 return Ok(Outcome::Rejected(Failure::AuthorityTrust));
388 }
389 let controls = facts
392 .authority()
393 .tbs()
394 .extensions()
395 .get(controls::IDENTIFIER, &work)
396 .map_err(Error::pkix)?
397 .map(|extension| AaControls::read_extension(extension, &work))
398 .transpose()?;
399 check(Authority::Direct(facts), controls, context, membership)
400}
401
402pub fn issued<'r, 'p, 'a, 'c, S: Evidence, W: Work, E>(
405 facts: &'r Issued<'r, 'p, 'c, 'a>,
406 context: Context<'r, 'p, 'a, 'c, '_, S, W>,
407 membership: impl FnMut(
408 GroupRole,
409 &GeneralName<'c>,
410 &Execution<'_>,
411 ) -> core::result::Result<target::Membership, E>,
412) -> core::result::Result<Outcome<Checked<'r, 'p, 'a, 'c, S>>, UseError<E>> {
413 check(Authority::Path(facts), None, context, membership)
414}
415
416fn holder_binding<W: Work>(
417 ac: &crate::attribute_certificate::AttributeCertificate<'_>,
418 holder: &Holder<'_, '_, '_, '_>,
419 inside: impl Fn(Interval) -> bool,
420 execution: &W,
421) -> Result<Option<Failure>> {
422 match holder {
423 Holder::Certificate { path, method } => {
424 if !inside(path.interval) {
425 return Ok(Some(Failure::Interval));
426 }
427 if let Check::Rejected(reason) =
428 ac.bind_holder(path.certificates[0], *method, execution)?
429 {
430 return Ok(Some(Failure::Holder(reason)));
431 }
432 }
433 Holder::Object { binding, path } => {
434 let original = binding.certificate();
435 charge(
436 &execution.execution_for(original.parser),
437 1,
438 original.element().span().start,
439 )?;
440 if !core::ptr::eq(binding.certificate(), ac) {
441 return Ok(Some(Failure::HolderObject));
442 }
443 if let Some(path) = path
444 && !inside(path.interval)
445 {
446 return Ok(Some(Failure::Interval));
447 }
448 match binding.object() {
449 super::digest::Object::Certificate(certificate) => {
450 charge(
451 &certificate.execution_in(execution),
452 1,
453 certificate.span().start,
454 )?;
455 if let Some(path) = path {
456 let actual = path.certificates[0];
457 charge(&actual.execution_in(execution), 1, actual.span().start)?;
458 }
459 if !path.is_some_and(|path| core::ptr::eq(path.certificates[0], certificate)) {
460 return Ok(Some(Failure::HolderPath));
461 }
462 }
463 super::digest::Object::PublicKey(key) => {
464 charge(
465 &key.key().execution_in(execution),
466 1,
467 key.key().algorithm().offset(),
468 )?;
469 if let Some(path) = path {
470 let actual = path.facts.key;
471 charge(
472 &actual.key().execution_in(execution),
473 1,
474 actual.key().algorithm().offset(),
475 )?;
476 let parameters = match (actual.parameters(), key.parameters()) {
477 (None, None) => true,
478 (Some(left), Some(right)) => core::ptr::eq(left, right),
479 _ => false,
480 };
481 if !core::ptr::eq(actual.key().as_bytes(), key.key().as_bytes())
482 || !parameters
483 {
484 return Ok(Some(Failure::HolderPath));
485 }
486 }
487 }
488 }
489 }
490 }
491 Ok(None)
492}
493
494fn check<'r, 'p, 'a, 'c, S: Evidence, W: Work, E>(
495 authority: Authority<'r, 'p, 'a, 'c>,
496 direct_controls: Option<AaControls<'a>>,
497 context: Context<'r, 'p, 'a, 'c, '_, S, W>,
498 mut membership: impl FnMut(
499 GroupRole,
500 &GeneralName<'c>,
501 &Execution<'_>,
502 ) -> core::result::Result<target::Membership, E>,
503) -> core::result::Result<Outcome<Checked<'r, 'p, 'a, 'c, S>>, UseError<E>> {
504 let facts = authority.facts();
505 let ac = facts.certificate();
506 let execution = context.execution.execution_for(ac.parser);
507 let at = ac.element().span().start;
508 let searches = u64::from(usize::BITS - context.utc.transitions().len().leading_zeros() + 1);
512 charge(&execution, 6 + 3 * searches + 64, at)?;
513 let upper = match context.now.upper_bound(context.utc) {
514 Ok(value) => value,
515 Err(reason) => return Ok(Outcome::Rejected(Failure::Time(reason))),
516 };
517 let inside = |interval: Interval| {
518 context.now.start() >= interval.from_instant() && upper <= interval.to_instant()
519 };
520 if !inside(facts.interval()) {
521 return Ok(Outcome::Rejected(Failure::Interval));
522 }
523 if let Some(reason) = holder_binding(ac, &context.holder, inside, &execution)? {
524 return Ok(Outcome::Rejected(reason));
525 }
526 let target = if let Some(targets) = facts.profile().targeting() {
527 match targets
528 .check(context.recipient, &execution, |name, work| {
529 membership(GroupRole::Target, name, work)
530 })
531 .map_err(UseError::Membership)?
532 {
533 target::Decision::Matches { target } => Some(target),
534 other => return Ok(Outcome::Rejected(Failure::Target(other))),
535 }
536 } else {
537 None
538 };
539 let proxy = if let Some(proxies) = facts.profile().proxying() {
540 let Some(proxy) = context.proxy else {
541 return Ok(Outcome::Rejected(Failure::ProxyContext));
542 };
543 match proxies
544 .check(
545 proxy.sender,
546 context.recipient,
547 proxy.intermediates,
548 &execution,
549 |subject, name, work| membership(GroupRole::Proxy(subject), name, work),
550 )
551 .map_err(UseError::Membership)?
552 {
553 proxy::Decision::Matches { group } => Some(group),
554 other => return Ok(Outcome::Rejected(Failure::Proxy(other))),
555 }
556 } else {
557 None
558 };
559 let status = context
560 .status
561 .check(facts.profile(), context.now, &execution)?;
562 if !matches!(status, Status::Good | Status::NoRevAvailable) {
563 return Ok(Outcome::Rejected(Failure::Status(status)));
564 }
565 let mut attributes = execution.vector();
566 let original = &ac.information().attributes;
567 attributes
568 .try_reserve(original.len())
569 .map_err(|e| Error::resource(e, super::FIELD, at))?;
570 for (index, attribute) in original.iter().enumerate() {
571 charge(&execution, 1, attribute.element().span().start)?;
572 let permission =
573 authority.allows(attribute.kind(), direct_controls.as_ref(), &execution)?;
574 attributes
575 .try_push(AttributeUse { index, permission })
576 .map_err(|e| Error::resource(e, super::FIELD, at))?;
577 }
578 Ok(Outcome::Checked(Checked {
579 authority,
580 direct_controls,
581 holder: context.holder,
582 status: context.status,
583 now: context.now,
584 target,
585 proxy,
586 attributes: attributes.into_owned(),
587 }))
588}