use super::{CheckedPath, exact};
use structured_asn1::memory::{MemoryError, Work};
use structured_pkix::{
crl::{AbsenceEvidence, Authenticated, EvidenceSource},
path::WorkingKey,
temporal::{LeapTable, Reading, Uncertainty},
};
#[derive(Clone, Copy)]
pub enum CrlAuthority<'view, 'document> {
Anchor,
Path(&'view CheckedPath<'document>),
}
#[derive(Clone, Copy)]
pub enum CrlAuthorities<'view, 'document> {
Complete(CrlAuthority<'view, 'document>),
Delta {
base: CrlAuthority<'view, 'document>,
delta: CrlAuthority<'view, 'document>,
},
}
pub struct CrlCoverage<'view, 'certificate, 'document> {
path: &'view CheckedPath<'document>,
member: usize,
coverage: &'view structured_pkix::crl::Coverage<'certificate, 'document>,
authorities: &'view [CrlAuthorities<'view, 'document>],
}
impl<'view, 'certificate, 'document> CrlCoverage<'view, 'certificate, 'document> {
#[must_use]
pub fn path(&self) -> &'view CheckedPath<'document> {
self.path
}
#[must_use]
pub fn member(&self) -> usize {
self.member
}
#[must_use]
pub fn coverage(&self) -> &'view structured_pkix::crl::Coverage<'certificate, 'document> {
self.coverage
}
#[must_use]
pub fn authorities(&self) -> &'view [CrlAuthorities<'view, 'document>] {
self.authorities
}
}
pub enum CrlCoverageBinding<'view, 'certificate, 'document> {
Bound(CrlCoverage<'view, 'certificate, 'document>),
Rejected {
source: usize,
cause: CrlBindingFailure,
},
}
#[derive(Debug)]
pub enum CrlCoverageError {
AuthorityCount,
Source {
source: usize,
cause: CrlBindingError,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrlBindingFailure {
Target,
Anchor,
Signer,
EffectiveKey,
Interval,
}
#[derive(Debug)]
pub enum CrlBindingError {
MemberIndex {
index: usize,
},
SourceKind,
Resource(MemoryError),
Name(structured_pkix::Error),
Time(Uncertainty),
}
impl From<super::authority::Error> for CrlBindingError {
fn from(cause: super::authority::Error) -> Self {
match cause {
super::authority::Error::Resource(cause) => Self::Resource(cause),
super::authority::Error::Name(cause) => Self::Name(cause),
super::authority::Error::Time(cause) => Self::Time(cause),
}
}
}
impl From<super::authority::Failure> for CrlBindingFailure {
fn from(cause: super::authority::Failure) -> Self {
match cause {
super::authority::Failure::Anchor => Self::Anchor,
super::authority::Failure::Signer => Self::Signer,
super::authority::Failure::EffectiveKey => Self::EffectiveKey,
super::authority::Failure::Interval => Self::Interval,
}
}
}
pub struct CrlTarget<'view, 'certificate, 'document> {
path: &'view CheckedPath<'document>,
member: usize,
evidence: &'view AbsenceEvidence<'certificate, 'document>,
authorities: CrlAuthorities<'view, 'document>,
}
impl<'view, 'certificate, 'document> CrlTarget<'view, 'certificate, 'document> {
#[must_use]
pub fn path(&self) -> &'view CheckedPath<'document> {
self.path
}
#[must_use]
pub fn member(&self) -> usize {
self.member
}
#[must_use]
pub fn evidence(&self) -> &'view AbsenceEvidence<'certificate, 'document> {
self.evidence
}
#[must_use]
pub fn authorities(&self) -> &CrlAuthorities<'view, 'document> {
&self.authorities
}
}
pub enum CrlBinding<'view, 'certificate, 'document> {
Bound(CrlTarget<'view, 'certificate, 'document>),
Rejected(CrlBindingFailure),
}
#[derive(Clone, Copy)]
pub(super) enum CrlSource<'view, 'certificate, 'document> {
Absence(&'view AbsenceEvidence<'certificate, 'document>),
Reading(super::CrlReading<'view, 'certificate, 'document>),
}
impl<'certificate, 'document> CrlSource<'_, 'certificate, 'document> {
fn target(self) -> &'certificate structured_pkix::Certificate<'document> {
match self {
Self::Absence(value) => value.target(),
Self::Reading(value) => value.target(),
}
}
fn at(self) -> Reading {
match self {
Self::Absence(value) => value.evaluated_at(),
Self::Reading(value) => value.evaluated_at(),
}
}
fn source(self) -> EvidenceSource<'certificate, 'document> {
match self {
Self::Absence(value) => value.source(),
Self::Reading(value) => value.source(),
}
}
}
impl<'document> CheckedPath<'document> {
pub(super) fn bind_crl_source<W: Work>(
&self,
member: usize,
facts: CrlSource<'_, '_, 'document>,
authorities: CrlAuthorities<'_, 'document>,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<Option<CrlBindingFailure>, CrlBindingError> {
execution
.charge_work(1)
.map_err(CrlBindingError::Resource)?;
let Some(expected) = self.members.get(member) else {
return Err(CrlBindingError::MemberIndex { index: member });
};
if !exact(
expected.certificate().as_bytes(),
facts.target().as_bytes(),
execution,
)
.map_err(CrlBindingError::Resource)?
{
return Ok(Some(CrlBindingFailure::Target));
}
match (facts.source(), authorities) {
(EvidenceSource::Complete(source), CrlAuthorities::Complete(authority)) => {
self.crl_authority(source, authority, facts.at(), utc, execution)
}
(
EvidenceSource::Delta { base, delta },
CrlAuthorities::Delta {
base: first,
delta: second,
},
) => {
if let Some(failure) =
self.crl_authority(base, first, facts.at(), utc, execution)?
{
return Ok(Some(failure));
}
self.crl_authority(delta, second, facts.at(), utc, execution)
}
_ => Err(CrlBindingError::SourceKind),
}
}
pub fn bind_crl_coverage<'view, 'certificate, W: Work>(
&'view self,
member: usize,
coverage: &'view structured_pkix::crl::Coverage<'certificate, 'document>,
authorities: &'view [CrlAuthorities<'view, 'document>],
utc: &LeapTable<'_>,
execution: &W,
) -> Result<CrlCoverageBinding<'view, 'certificate, 'document>, CrlCoverageError> {
execution
.charge_work(1)
.map_err(|cause| CrlCoverageError::Source {
source: 0,
cause: CrlBindingError::Resource(cause),
})?;
if authorities.len() != coverage.sources().len() {
return Err(CrlCoverageError::AuthorityCount);
}
for (source, authority) in authorities.iter().enumerate() {
execution
.charge_work(1)
.map_err(|cause| CrlCoverageError::Source {
source,
cause: CrlBindingError::Resource(cause),
})?;
match self
.bind_crl(
member,
&coverage.sources()[source],
*authority,
utc,
execution,
)
.map_err(|cause| CrlCoverageError::Source { source, cause })?
{
CrlBinding::Bound(_) => {}
CrlBinding::Rejected(cause) => {
return Ok(CrlCoverageBinding::Rejected { source, cause });
}
}
}
Ok(CrlCoverageBinding::Bound(CrlCoverage {
path: self,
member,
coverage,
authorities,
}))
}
pub fn bind_crl<'view, 'certificate, W: Work>(
&'view self,
member: usize,
evidence: &'view AbsenceEvidence<'certificate, 'document>,
authorities: CrlAuthorities<'view, 'document>,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<CrlBinding<'view, 'certificate, 'document>, CrlBindingError> {
if let Some(failure) = self.bind_crl_source(
member,
CrlSource::Absence(evidence),
authorities,
utc,
execution,
)? {
return Ok(CrlBinding::Rejected(failure));
}
Ok(CrlBinding::Bound(CrlTarget {
path: self,
member,
evidence,
authorities,
}))
}
fn crl_authority<W: Work>(
&self,
source: Authenticated<'_, 'document>,
authority: CrlAuthority<'_, 'document>,
at: Reading,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<Option<CrlBindingFailure>, CrlBindingError> {
let resource = CrlBindingError::Resource;
execution.charge_work(1).map_err(resource)?;
let expected = match authority {
CrlAuthority::Anchor => {
if !self
.anchor
.name()
.matches(source.issuer().tbs().subject(), execution)
.map_err(CrlBindingError::Name)?
{
return Ok(Some(CrlBindingFailure::Signer));
}
WorkingKey::standing(self.anchor.key())
}
CrlAuthority::Path(path) => {
return Ok(self
.path_authority(path, source.issuer(), source.key(), at, utc, execution)?
.map(Into::into));
}
};
if !exact(
expected.key().as_bytes(),
source.key().key().as_bytes(),
execution,
)
.map_err(resource)?
{
return Ok(Some(CrlBindingFailure::Signer));
}
let same = match (expected.parameters(), source.key().parameters()) {
(None, None) => true,
(Some(left), Some(right)) => exact(left, right, execution).map_err(resource)?,
_ => false,
};
if same {
return Ok(None);
}
Ok(Some(CrlBindingFailure::EffectiveKey))
}
}