use crate::role;
use crate::xml::{Access, AccessError, Snapshot, Xml};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
use structured_ltv::pki::{self as ltv, Authorities, CredentialGroup, CredentialReceipts};
#[derive(Debug, Clone, Copy)]
pub struct Credential<N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
entry: role::Entry<N>,
}
impl<N: Copy, D: Copy, G: Copy> Credential<N, D, G> {
#[must_use]
pub fn snapshot(self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(self) -> N {
self.signature
}
#[must_use]
pub fn entry(self) -> role::Entry<N> {
self.entry
}
}
pub use structured_ltv::credential::Authentication;
pub trait Provider<'path, 'document, X: Xml> {
type Evidence;
type Error;
fn authenticate<W: Work>(
&self,
access: &Access<'_, X, W>,
credential: Credential<X::Id, X::Document, X::Generation>,
) -> Result<Authentication<'path, 'document, Self::Evidence>, Self::Error>;
}
pub struct Checked<'path, 'document, E, N, D, G> {
source: Credential<N, D, G>,
evidence: E,
authorities: Authorities<'path, 'document>,
}
impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> Checked<'path, 'document, E, N, D, G> {
#[must_use]
pub fn source(&self) -> Credential<N, D, G> {
self.source
}
#[must_use]
pub fn evidence(&self) -> &E {
&self.evidence
}
#[must_use]
pub fn authorities(&self) -> Authorities<'path, 'document> {
self.authorities
}
}
pub struct Credentials<'path, 'document, E, N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
entries: OwnedVec<Checked<'path, 'document, E, N, D, G>>,
}
impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> Credentials<'path, 'document, E, N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn entries(&self) -> &[Checked<'path, 'document, E, N, D, G>] {
&self.entries
}
}
impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> CredentialGroup<'path, 'document>
for Credentials<'path, 'document, E, N, D, G>
{
type Id = N;
fn signature(&self) -> N {
self.signature
}
fn len(&self) -> usize {
self.entries.len()
}
fn credential(&self, ordinal: usize) -> ltv::Credential<'path, 'document, N> {
let checked = &self.entries[ordinal];
ltv::Credential {
signature: checked.source.signature,
node: checked.source.entry.node,
authorities: checked.authorities,
}
}
}
#[derive(Debug)]
pub enum Error<E, PE, N> {
Foreign {
signature: N,
},
Access(AccessError<E, N>),
Unsupported {
node: N,
},
Rejected {
node: N,
},
MissingAuthorities {
node: N,
},
Provider {
node: N,
cause: PE,
},
Resource {
node: N,
cause: MemoryError,
},
}
pub type CheckResult<'path, 'document, X, P> = Result<
Credentials<
'path,
'document,
<P as Provider<'path, 'document, X>>::Evidence,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
Error<<X as Xml>::Error, <P as Provider<'path, 'document, X>>::Error, <X as Xml>::Id>,
>;
pub fn check<'path, 'document, X: Xml, P: Provider<'path, 'document, X>, W: Work>(
roles: &role::Catalogue<X>,
provider: &P,
access: &Access<'_, X, W>,
) -> CheckResult<'path, 'document, X, P> {
if roles.snapshot() != access.snapshot() {
return Err(Error::Access(AccessError::Changed { node: roles.node() }));
}
let mut entries = access
.query(roles.node(), |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
for entry in roles.entries() {
access
.query(entry.node, |_, _, work| Ok(work.charge_work(1)))
.map_err(Error::Access)?
.map_err(|cause| Error::Resource {
node: entry.node,
cause,
})?;
if entry.kind == role::Kind::Claimed {
continue;
}
let source = Credential {
snapshot: roles.snapshot(),
signature: roles.signature(),
entry: *entry,
};
let outcome = access
.query(entry.node, |_, _, _| {
Ok(provider.authenticate(access, source))
})
.map_err(Error::Access)?
.map_err(|cause| Error::Provider {
node: entry.node,
cause,
})?;
let (evidence, authorities) = match outcome {
Authentication::Unsupported => return Err(Error::Unsupported { node: entry.node }),
Authentication::Rejected => return Err(Error::Rejected { node: entry.node }),
Authentication::Authenticated {
evidence,
authorities,
} => (evidence, authorities),
};
if let Authorities::Paths(paths) = authorities
&& paths.is_empty()
{
return Err(Error::MissingAuthorities { node: entry.node });
}
entries
.try_push(Checked {
source,
evidence,
authorities,
})
.map_err(|cause| Error::Resource {
node: entry.node,
cause,
})?;
}
access
.query(roles.node(), |_, _, _| Ok(()))
.map_err(Error::Access)?;
Ok(Credentials {
snapshot: roles.snapshot(),
signature: roles.signature(),
entries: entries.into_owned(),
})
}
pub struct CounterCredentials<'path, 'document, E, N, D, G> {
source: crate::counter::Checked<N, D, G>,
credentials: Credentials<'path, 'document, E, N, D, G>,
}
impl<'path, 'document, E, N, D, G> CounterCredentials<'path, 'document, E, N, D, G> {
#[must_use]
pub fn source(&self) -> &crate::counter::Checked<N, D, G> {
&self.source
}
#[must_use]
pub fn credentials(&self) -> &Credentials<'path, 'document, E, N, D, G> {
&self.credentials
}
}
pub struct ParticipantCredentials<'path, 'document, E, N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
root: Option<Credentials<'path, 'document, E, N, D, G>>,
counters: OwnedVec<CounterCredentials<'path, 'document, E, N, D, G>>,
}
impl<'path, 'document, E, N: Copy, D: Copy, G: Copy>
ParticipantCredentials<'path, 'document, E, N, D, G>
{
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn root(&self) -> Option<&Credentials<'path, 'document, E, N, D, G>> {
self.root.as_ref()
}
#[must_use]
pub fn counters(&self) -> &[CounterCredentials<'path, 'document, E, N, D, G>] {
&self.counters
}
}
impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> CredentialReceipts<'path, 'document>
for ParticipantCredentials<'path, 'document, E, N, D, G>
{
type Id = N;
type Document = D;
type Generation = G;
type Group = Credentials<'path, 'document, E, N, D, G>;
fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
fn signature(&self) -> N {
self.signature
}
fn root(&self) -> Option<&Self::Group> {
self.root.as_ref()
}
fn counters(&self) -> usize {
self.counters.len()
}
fn counter(&self, ordinal: usize) -> (N, &Self::Group) {
let counter = &self.counters[ordinal];
(counter.source.original().child(), &counter.credentials)
}
}
pub type ParticipantResult<'path, 'document, X, P> = Result<
ParticipantCredentials<
'path,
'document,
<P as Provider<'path, 'document, X>>::Evidence,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
Error<<X as Xml>::Error, <P as Provider<'path, 'document, X>>::Error, <X as Xml>::Id>,
>;
pub fn check_participants<'path, 'document, X: Xml, P: Provider<'path, 'document, X>, W: Work>(
prepared: &crate::prepare::Prepared<X>,
counters: &crate::pki::CounterPaths<'_, '_, X::Id, X::Document, X::Generation>,
provider: &P,
access: &Access<'_, X, W>,
) -> ParticipantResult<'path, 'document, X, P> {
use structured_asn1::memory::HashWork;
let layout = prepared.bound().containers();
let signature = layout.signature();
if layout.snapshot() != access.snapshot() || counters.snapshot() != access.snapshot() {
return Err(Error::Access(AccessError::Changed { node: signature }));
}
let mut groups = access
.query(signature, |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
let cost = signature
.hash_work()
.and_then(|cost| {
cost.checked_add(counters.signature().hash_work()?)
.ok_or(MemoryError::WorkExceeded)
})
.map_err(|cause| Error::Resource {
node: signature,
cause,
})?;
access
.execution()
.charge_work(cost)
.map_err(|cause| Error::Resource {
node: signature,
cause,
})?;
if signature != counters.signature() {
return Err(Error::Foreign { signature });
}
let root = match prepared.roles() {
Some(roles) => Some(check(roles, provider, access)?),
None => None,
};
for actor in counters.actors() {
access
.query(signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(Error::Access)?
.map_err(|cause| Error::Resource {
node: signature,
cause,
})?;
let Some(roles) = actor.roles() else {
continue;
};
let credentials = check(roles, provider, access)?;
groups
.try_push(CounterCredentials {
source: *actor.signer().counter(),
credentials,
})
.map_err(|cause| Error::Resource {
node: roles.node(),
cause,
})?;
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(Error::Access)?;
Ok(ParticipantCredentials {
snapshot: layout.snapshot(),
signature,
root,
counters: groups.into_owned(),
})
}