use super::Unified;
use crate::{
basic::Basic,
credential::{Complete, Group},
query::Query,
range::RangeSource,
timestamp::{Node, Pades},
};
use structured_asn1::memory::{Memory, MemoryError, OwnedVec, Work};
use structured_cades::material::credentials::{Need, Provider};
use structured_ltv::{
baseline::CredentialMaterial,
material::{
ArchiveError, Archived, Coverage, CoverageError, DependencyError, DependencyReport,
DependencyVisit, IndexError, Missing,
},
pki::{CounterReceipts, CredentialGroup},
source::{Query as _, ScopeError, Snapshot},
timestamp::{CheckedActor, TimestampRequirements, TimestampScope},
};
pub type CheckedReport<'a, 'r, 'm, 'd, 'p, D, G, E> =
Report<'a, Complete<'r, 'm, 'd, 'p, D, G, E>, D, G>;
pub enum Scope<'a, 'c, 'd, D, G, W: Work = Memory> {
Whole,
Before {
coverage: &'a Coverage<Node, D, G, W, Pades>,
actor: &'a CheckedActor<'c, 'd, Node, D, G, Pades>,
},
}
impl<D, G> Scope<'_, '_, '_, D, G> {
#[must_use]
pub fn whole() -> Self {
Self::Whole
}
}
pub struct Entry {
pub node: Node,
pub dependencies: DependencyReport,
}
pub struct Report<'a, C, D, G> {
credentials: &'a C,
scope: TimestampScope<Node, D, G>,
entries: OwnedVec<Entry>,
complete: bool,
}
impl<C, D, G> Report<'_, C, D, G> {
#[must_use]
pub fn entries(&self) -> &[Entry] {
&self.entries
}
}
impl<C, D: Copy, G: Copy> CredentialMaterial for Report<'_, C, D, G> {
type Id = Node;
type Document = D;
type Generation = G;
type Credentials = C;
fn scope(&self) -> TimestampScope<Node, D, G> {
self.scope
}
fn snapshot(&self) -> Snapshot<D, G> {
self.scope.location().snapshot
}
fn credentials(&self) -> &C {
self.credentials
}
fn all_requirements_met(&self) -> bool {
self.complete
}
}
#[derive(Debug)]
pub enum Error<E> {
Foreign {
node: Node,
},
Scope(ScopeError<Node>),
Obligation(structured_ltv::timestamp::ObligationError<Node>),
Index(IndexError<Node>),
Archive(ArchiveError<Node>),
Coverage(CoverageError<Node>),
Resource {
node: Node,
cause: MemoryError,
},
Cms(structured_cms::Error),
Provider {
node: Node,
cause: E,
},
CallbackProtocol {
node: Node,
},
}
fn dependency<D: Copy + Eq, G: Copy + Eq, E, S, W: Work, O: Work>(
need: Need<'_, '_, '_, '_>,
mapping: crate::binding::Mapping<'_, '_, D, G>,
index: &Unified<'_, '_, D, G>,
scope: &Scope<'_, '_, '_, D, G, W>,
open: &structured_asn1::memory::OwnedMap<Node, (), O>,
node: Node,
query: &Query<'_, '_, S>,
) -> Result<Option<Missing>, Error<E>>
where
S: RangeSource<Document = D, Generation = G>,
{
match need {
Need::Missing => Ok(Some(Missing::Absent)),
Need::Known(artifact) => {
if let Scope::Before { coverage, actor } = scope {
return index
.index
.archived(artifact, coverage, actor.verified(), query)
.map(|value| match value {
Archived::Present(_) => None,
Archived::Missing => Some(Missing::Absent),
Archived::Uncovered => Some(Missing::Uncovered),
})
.map_err(Error::Archive);
}
let mut copies = index
.index
.artifact(artifact, query)
.map_err(Error::Index)?;
Ok(
if copies.next().transpose().map_err(Error::Index)?.is_some() {
None
} else {
Some(Missing::Absent)
},
)
}
Need::Open(location) => {
if location.snapshot != mapping.cms {
return Err(Error::Foreign { node });
}
let node = mapping.node(location.node);
if open
.get(&node, query.execution())
.map_err(|cause| Error::Resource { node, cause })?
.is_none()
{
return Ok(Some(Missing::Absent));
}
if let Scope::Before { coverage, actor } = scope {
return coverage
.covers(actor.verified(), node, query)
.map(|covered| {
if covered {
None
} else {
Some(Missing::Uncovered)
}
})
.map_err(Error::Coverage);
}
Ok(None)
}
}
}
fn scope_binding<
D: Copy + Eq,
G: Copy + Eq,
E,
S: RangeSource<Document = D, Generation = G>,
W: Work,
>(
scope: &Scope<'_, '_, '_, D, G, W>,
required: &TimestampRequirements<Node, D, G, Pades>,
index: &Unified<'_, '_, D, G>,
root: Node,
query: &Query<'_, '_, S>,
) -> Result<(), Error<E>> {
match (scope, required.scope()) {
(Scope::Whole, TimestampScope::All(_)) if core::ptr::eq(required, index.required) => {}
(Scope::Before { coverage, actor }, TimestampScope::BeforeArchive(location))
if location == actor.verified().token() && actor.verified().signature() == root =>
{
coverage
.require_evidence(actor.verified(), actor.verified().property(), query)
.map_err(Error::Coverage)?;
}
_ => return Err(Error::Foreign { node: root }),
}
Ok(())
}
pub fn check<'a, 'r, 'm, 'd, 'p, D: Copy + Eq, G: Copy + Eq, E, P, S, W>(
basic: &Basic<'_, '_, '_, 'm, 'd, '_, '_, 'p, D, G>,
credentials: &'a Complete<'r, 'm, 'd, 'p, D, G, E>,
required: &TimestampRequirements<Node, D, G, Pades>,
index: &Unified<'_, 'd, D, G>,
scope: &Scope<'_, '_, '_, D, G, W>,
provider: &P,
query: &Query<'_, '_, S>,
) -> Result<CheckedReport<'a, 'r, 'm, 'd, 'p, D, G, E>, Error<P::Error>>
where
P: Provider<E>,
S: RangeSource<Document = D, Generation = G>,
W: Work,
{
let bound = basic.binding();
let root = bound.mapping.root;
let snapshot = bound.mapping.snapshot;
query.admit(root, 1).map_err(Error::Scope)?;
if !query.belongs(bound.prepared().container.signature().owner().value.session)
|| !query.belongs(index.session)
|| query.snapshot() != snapshot
|| credentials.mapping.snapshot != snapshot
|| credentials.mapping.cms != bound.mapping.cms
|| credentials.mapping.root != root
|| required.signature() != root
|| required.scope().location().snapshot != snapshot
{
return Err(Error::Foreign { node: root });
}
index
.index
.require_signature(root, query)
.map_err(Error::Index)?;
scope_binding(scope, required, index, root, query)?;
required
.includes_counters(basic.counters(), query)
.map_err(Error::Scope)?;
let mut open = query.execution().map();
for value in index.index.open() {
query.admit(root, 1).map_err(Error::Scope)?;
let node = bound.mapping.node(value.node);
open.try_insert(node, ())
.map_err(|cause| Error::Resource { node, cause })?;
}
let fields = bound.prepared().cms.signer().fields_source();
let reader = fields.reader_in(query.execution()).map_err(Error::Cms)?;
let mut entries = query.execution().vector();
let mut complete = true;
let mut visit = |group: &Group<'_, 'm, 'd, 'p, D, G, E>| -> Result<(), Error<P::Error>> {
for credential in group.common.entries() {
query.admit(root, 1).map_err(Error::Scope)?;
let node = bound.mapping.node(credential.source().node);
let mut visiting =
DependencyVisit::new(credential.source().node, query.execution(), |need| {
dependency(need, bound.mapping, index, scope, &open, node, query)
})
.map_err(|cause| Error::Resource { node, cause })?;
let answer = provider.visit(credential, index.index.open(), &reader, |need| {
visiting.observe(need)
});
let dependencies = visiting
.finish(answer)
.map_err(|cause| dependency_error(node, cause))?;
if dependencies.completeness != structured_ltv::material::Completeness::Complete
|| !dependencies.findings.is_empty()
{
complete = false;
}
entries
.try_push(Entry { node, dependencies })
.map_err(|cause| Error::Resource { node, cause })?;
}
Ok(())
};
if let Some(group) = &credentials.root {
visit(group)?;
}
let mut counter = 0;
for group in &credentials.counters {
loop {
query.admit(root, 1).map_err(Error::Scope)?;
if counter == basic.counters().len() {
return Err(Error::Foreign {
node: group.signature(),
});
}
let ordinal = counter;
counter += 1;
if basic.counters().counter(ordinal).signature != group.signature() {
continue;
}
if required
.counter_in_scope(ordinal, query)
.map_err(Error::Obligation)?
{
visit(group)?;
}
break;
}
}
query.admit(root, 1).map_err(Error::Scope)?;
Ok(Report {
credentials,
scope: required.scope(),
entries: entries.into_owned(),
complete,
})
}
fn dependency_error<N, E>(node: Node, cause: DependencyError<N, Error<E>, E>) -> Error<E> {
match cause {
DependencyError::Processing { cause, .. } => cause,
DependencyError::Resource { cause, .. } => Error::Resource { node, cause },
DependencyError::Provider { cause, .. } => Error::Provider { node, cause },
DependencyError::CallbackProtocol { .. } => Error::CallbackProtocol { node },
}
}