use super::target::{CheckError, Membership, TargetInformation};
use crate::{Error, Result};
use structured_asn1::memory::{Execution, Work};
use structured_asn1::{Element, Oid, Parser};
use structured_pkix::Extension;
use structured_pkix::general_name::GeneralName;
const FIELD: &str = "attributeCertificate.proxyInfo";
pub const IDENTIFIER: Oid<'static> = Oid::from_bytes(&[43, 6, 1, 5, 5, 7, 1, 10]);
#[derive(Debug, Clone, Copy)]
pub enum Sender<'a, 'n> {
Holder,
Proxy(&'n [GeneralName<'a>]),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Subject {
Sender,
Recipient,
Intermediate(usize),
}
#[derive(Debug)]
pub struct ProxyInfo<'a> {
parser: &'a Parser,
targets: TargetInformation<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decision {
Matches {
group: usize,
},
Differs,
Unproven,
NonCritical,
ProhibitedTargetCertificate {
target: usize,
},
}
impl<'a> ProxyInfo<'a> {
pub fn read<W: Work>(extension: &Extension<'a>, work: &W) -> Result<Self> {
Ok(Self {
parser: extension.parser(),
targets: TargetInformation::read_as(extension, work, IDENTIFIER, FIELD)?,
})
}
#[must_use]
pub fn element(&self) -> Element<'a> {
self.targets.element()
}
#[must_use]
pub fn groups(&self) -> &[super::target::Group<'a>] {
self.targets.groups()
}
#[must_use]
pub fn targets(&self) -> &[super::target::Target<'a>] {
self.targets.targets()
}
#[must_use]
pub fn certificates(&self) -> &[super::target::TargetCertificate<'a>] {
self.targets.certificates()
}
#[must_use]
pub fn is_critical(&self) -> bool {
self.targets.is_critical()
}
pub fn check<W: Work, E>(
&self,
sender: Sender<'_, '_>,
recipient: &[GeneralName<'_>],
intermediates: &[&[GeneralName<'_>]],
work: &W,
mut membership: impl FnMut(
Subject,
&GeneralName<'a>,
&Execution<'_>,
) -> core::result::Result<Membership, E>,
) -> core::result::Result<Decision, CheckError<E>> {
let execution = work.execution_for(self.parser);
let pay = |at| {
execution
.charge_work(1)
.map_err(|e| CheckError::Processing(Error::resource(e, FIELD, at)))
};
pay(self.targets.element().span().start)?;
if !self.targets.is_critical() {
return Ok(Decision::NonCritical);
}
if let Some(entry) = self.targets.certificates().first() {
return Ok(Decision::ProhibitedTargetCertificate {
target: entry.target,
});
}
let mut unknown = false;
for (index, group) in self.targets.groups().iter().enumerate() {
pay(group.element.span().start)?;
let mut undecided = false;
match self.matches(
index,
Subject::Recipient,
recipient,
&execution,
&mut membership,
)? {
Membership::NotMember => continue,
Membership::Unknown => undecided = true,
Membership::Member => {}
}
if let Sender::Proxy(names) = sender {
match self.matches(index, Subject::Sender, names, &execution, &mut membership)? {
Membership::NotMember => continue,
Membership::Unknown => undecided = true,
Membership::Member => {}
}
}
let mut excluded = false;
for (ordinal, names) in intermediates.iter().enumerate() {
pay(group.element.span().start)?;
match self.matches(
index,
Subject::Intermediate(ordinal),
names,
&execution,
&mut membership,
)? {
Membership::NotMember => {
excluded = true;
break;
}
Membership::Unknown => undecided = true,
Membership::Member => {}
}
}
if excluded {
continue;
}
if !undecided {
return Ok(Decision::Matches { group: index });
}
unknown = true;
}
Ok(if unknown {
Decision::Unproven
} else {
Decision::Differs
})
}
fn matches<E>(
&self,
group: usize,
subject: Subject,
names: &[GeneralName<'_>],
execution: &Execution<'_>,
membership: &mut impl FnMut(
Subject,
&GeneralName<'a>,
&Execution<'_>,
) -> core::result::Result<Membership, E>,
) -> core::result::Result<Membership, CheckError<E>> {
let mut unknown = false;
for target in &self.targets.targets()[self.targets.groups()[group].targets.clone()] {
match super::target::matches_target(
target,
names,
execution,
FIELD,
&mut |name, work| membership(subject, name, work),
)? {
Membership::Member => return Ok(Membership::Member),
Membership::Unknown => unknown = true,
Membership::NotMember => {}
}
}
Ok(if unknown {
Membership::Unknown
} else {
Membership::NotMember
})
}
}