use super::origin::Origin;
use super::{AuthorityClosure, AuthorityClosureError, AuthorityNode, CheckedPath, PathStatuses};
use crate::source::{Location, Query, ScopeError, Snapshot};
use crate::timestamp::{ObligationError, TimestampPaths, TimestampScope};
use core::marker::PhantomData;
use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
use structured_pkix::path::EffectiveAnchor;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Participant<N> {
Signer(N),
Counter {
signature: N,
property: N,
},
Credential {
signature: N,
node: N,
authority: usize,
},
Timestamp {
property: N,
token: N,
},
}
#[derive(Clone, Copy)]
pub enum ParticipantAuthority<'path, 'document> {
Path(&'path CheckedPath<'document>),
Anchor(&'path EffectiveAnchor<'document>),
CertificateFree,
}
#[derive(Clone, Copy)]
pub enum Authorities<'path, 'document> {
Paths(&'path [&'path CheckedPath<'document>]),
DirectAnchor(&'path EffectiveAnchor<'document>),
CertificateFree,
}
pub trait SignerReceipt<'path, 'document> {
type Id: Copy;
type Document: Copy;
type Generation: Copy;
fn signature(&self) -> Location<Self::Id, Self::Document, Self::Generation>;
fn path(&self) -> Option<&'path CheckedPath<'document>>;
}
#[derive(Clone, Copy)]
pub struct Counter<'path, 'document, N> {
pub signature: N,
pub property: N,
pub path: &'path CheckedPath<'document>,
}
pub trait CounterReceipts<'path, 'document> {
type Id: Copy;
type Document: Copy;
type Generation: Copy;
fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
fn signature(&self) -> Self::Id;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn counter(&self, ordinal: usize) -> Counter<'path, 'document, Self::Id>;
}
#[derive(Clone, Copy)]
pub struct Credential<'path, 'document, N> {
pub signature: N,
pub node: N,
pub authorities: Authorities<'path, 'document>,
}
pub trait CredentialGroup<'path, 'document> {
type Id: Copy;
fn signature(&self) -> Self::Id;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn credential(&self, ordinal: usize) -> Credential<'path, 'document, Self::Id>;
}
pub trait CredentialReceipts<'path, 'document> {
type Id: Copy;
type Document: Copy;
type Generation: Copy;
type Group: CredentialGroup<'path, 'document, Id = Self::Id>;
fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
fn signature(&self) -> Self::Id;
fn root(&self) -> Option<&Self::Group>;
fn counters(&self) -> usize;
fn counter(&self, ordinal: usize) -> (Self::Id, &Self::Group);
}
pub struct ParticipantRequirement<'path, 'document, N> {
participant: Participant<N>,
authority: ParticipantAuthority<'path, 'document>,
}
impl<'path, 'document, N: Copy> ParticipantRequirement<'path, 'document, N> {
#[must_use]
pub fn participant(&self) -> Participant<N> {
self.participant
}
#[must_use]
pub fn authority(&self) -> ParticipantAuthority<'path, 'document> {
self.authority
}
}
pub struct ParticipantRequirements<'path, 'document, N, D, G, I> {
scope: TimestampScope<N, D, G>,
signature: N,
entries: OwnedVec<ParticipantRequirement<'path, 'document, N>>,
origins: Origins<'path>,
issuer: PhantomData<I>,
}
struct Origins<'a> {
signer: Origin<'a>,
counters: Origin<'a>,
credentials: Origin<'a>,
timestamps: Origin<'a>,
}
impl<'path, 'document, N: Copy, D: Copy, G: Copy, I>
ParticipantRequirements<'path, 'document, N, D, G, I>
{
#[must_use]
pub fn scope(&self) -> TimestampScope<N, D, G> {
self.scope
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn entries(&self) -> &[ParticipantRequirement<'path, 'document, N>] {
&self.entries
}
#[must_use]
pub fn same_credentials<C>(&self, credentials: &C) -> bool {
self.origins.credentials.matches(credentials)
}
pub fn require_inputs<S, C, R, Q>(
&self,
signer: &S,
counters: &C,
credentials: &R,
timestamps: &TimestampPaths<'_, '_, '_, N, D, G, I>,
query: &Q,
) -> Result<(), ParticipantError<N>>
where
D: Eq,
G: Eq,
Q: Query<Id = N, Document = D, Generation = G>,
{
let signature = self.signature;
if self.scope.location().snapshot != query.snapshot() {
return Err(ParticipantError::Foreign { signature });
}
query.admit(signature, 5)?;
if !self.origins.signer.matches(signer)
|| !self.origins.counters.matches(counters)
|| !self.same_credentials(credentials)
|| !self.origins.timestamps.matches(timestamps)
{
return Err(ParticipantError::Foreign { signature });
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParticipantError<N> {
CounterTimestamps,
Obligation(ObligationError<N>),
Scope(ScopeError<N>),
Foreign {
signature: N,
},
UnprovenSigner,
MissingAuthorities {
node: N,
},
Resource {
node: N,
cause: MemoryError,
},
}
pub type ParticipantResult<'path, 'document, Q, I> = Result<
ParticipantRequirements<
'path,
'document,
<Q as Query>::Id,
<Q as Query>::Document,
<Q as Query>::Generation,
I,
>,
ParticipantError<<Q as Query>::Id>,
>;
impl<N> From<ScopeError<N>> for ParticipantError<N> {
fn from(cause: ScopeError<N>) -> Self {
Self::Scope(cause)
}
}
pub struct ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I> {
requirements: &'requirements ParticipantRequirements<'view, 'document, N, D, G, I>,
authorities: AuthorityClosure<'view, 'certificate, 'document>,
}
impl<'requirements, 'view, 'certificate, 'document, N, D, G, I>
ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>
{
#[must_use]
pub fn requirements(
&self,
) -> &'requirements ParticipantRequirements<'view, 'document, N, D, G, I> {
self.requirements
}
#[must_use]
pub fn authorities(&self) -> &AuthorityClosure<'view, 'certificate, 'document> {
&self.authorities
}
}
#[derive(Debug)]
pub enum ParticipantClosureError<N> {
Scope(ScopeError<N>),
Foreign,
Count,
Path {
participant: usize,
},
Authority(AuthorityClosureError),
Resource(MemoryError),
}
pub type ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I> = Result<
ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
ParticipantClosureError<N>,
>;
impl<'path, 'document, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, I>
ParticipantRequirements<'path, 'document, N, D, G, I>
{
pub fn close_statuses<'requirements, 'view, 'certificate, Q>(
&'requirements self,
roots: &'view [&'view PathStatuses<'view, 'certificate, 'document>],
supplied: &'view [AuthorityNode<'view, 'certificate, 'document>],
query: &Q,
) -> ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I>
where
Q: Query<Id = N, Document = D, Generation = G>,
'path: 'view,
{
if self.scope.location().snapshot != query.snapshot() {
return Err(ParticipantClosureError::Foreign);
}
query
.admit(self.signature, 1)
.map_err(ParticipantClosureError::Scope)?;
let work = query.execution();
let mut cursor = 0;
for (participant, entry) in self.entries.iter().enumerate() {
work.charge_work(1)
.map_err(ParticipantClosureError::Resource)?;
let ParticipantAuthority::Path(path) = entry.authority else {
continue;
};
let Some(root) = roots.get(cursor) else {
return Err(ParticipantClosureError::Count);
};
if !core::ptr::eq(path, root.path()) {
return Err(ParticipantClosureError::Path { participant });
}
cursor += 1;
}
if cursor != roots.len() {
return Err(ParticipantClosureError::Count);
}
let authorities = super::close_authorities(roots, supplied, work)
.map_err(ParticipantClosureError::Authority)?;
Ok(ParticipantClosure {
requirements: self,
authorities,
})
}
}
struct Collect<'path, 'document, N, W: Work> {
entries: OwnedVec<ParticipantRequirement<'path, 'document, N>, W>,
}
impl<'path, 'document, N: Copy, W: Work> Collect<'path, 'document, N, W> {
fn push<Q: Query<Id = N, Work = W>>(
&mut self,
participant: Participant<N>,
authority: ParticipantAuthority<'path, 'document>,
node: N,
query: &Q,
) -> Result<(), ParticipantError<N>> {
query.admit(node, 2)?;
self.entries
.try_push(ParticipantRequirement {
participant,
authority,
})
.map_err(|cause| ParticipantError::Resource { node, cause })
}
fn credentials<Q: Query<Id = N, Work = W>, R: CredentialGroup<'path, 'document, Id = N>>(
&mut self,
credentials: &R,
query: &Q,
) -> Result<(), ParticipantError<N>> {
for ordinal in 0..credentials.len() {
query.admit(credentials.signature(), 2)?;
let credential = credentials.credential(ordinal);
let node = credential.node;
let participant = |authority| Participant::Credential {
signature: credential.signature,
node,
authority,
};
match credential.authorities {
Authorities::Paths([]) => {
return Err(ParticipantError::MissingAuthorities { node });
}
Authorities::Paths(paths) => {
for (index, path) in paths.iter().enumerate() {
self.push(
participant(index),
ParticipantAuthority::Path(path),
node,
query,
)?;
}
}
Authorities::DirectAnchor(anchor) => self.push(
participant(0),
ParticipantAuthority::Anchor(anchor),
node,
query,
)?,
Authorities::CertificateFree => self.push(
participant(0),
ParticipantAuthority::CertificateFree,
node,
query,
)?,
}
}
Ok(())
}
}
fn counter_and_role_requirements<'path, 'document, C, R, Q, I>(
collect: &mut Collect<'path, 'document, Q::Id, Q::Work>,
counters: &C,
credentials: &R,
timestamps: &TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
query: &Q,
) -> Result<(), ParticipantError<Q::Id>>
where
Q: Query,
C: CounterReceipts<'path, 'document, Id = Q::Id>,
R: CredentialReceipts<'path, 'document, Id = Q::Id>,
{
let signature = counters.signature();
let mut ordinal = 0;
while ordinal < counters.len() {
query.admit(signature, 2)?;
let selected = timestamps
.required()
.counter_in_scope(ordinal, query)
.map_err(ParticipantError::Obligation)?;
let counter = counters.counter(ordinal);
ordinal += 1;
if !selected {
continue;
}
collect.push(
Participant::Counter {
signature: counter.signature,
property: counter.property,
},
ParticipantAuthority::Path(counter.path),
counter.property,
query,
)?;
}
if let Some(root) = credentials.root() {
collect.credentials(root, query)?;
}
let mut credential = 0;
let mut ordinal = 0;
while ordinal < counters.len() && credential < credentials.counters() {
query.admit(signature, 2)?;
let (child, group) = credentials.counter(credential);
let same = query
.equal(counters.counter(ordinal).signature, child)
.map_err(|cause| ParticipantError::Resource {
node: signature,
cause,
})?;
if same {
if timestamps
.required()
.counter_in_scope(ordinal, query)
.map_err(ParticipantError::Obligation)?
{
collect.credentials(group, query)?;
}
credential += 1;
}
ordinal += 1;
}
if credential != credentials.counters() {
return Err(ParticipantError::Foreign { signature });
}
Ok(())
}
pub fn participants<'path, 'document, S, C, R, Q, I>(
_issuer: I,
signer: &'path S,
counters: &'path C,
credentials: &'path R,
timestamps: &'path TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
query: &Q,
) -> ParticipantResult<'path, 'document, Q, I>
where
Q: Query,
S: SignerReceipt<
'path,
'document,
Id = Q::Id,
Document = Q::Document,
Generation = Q::Generation,
>,
C: CounterReceipts<
'path,
'document,
Id = Q::Id,
Document = Q::Document,
Generation = Q::Generation,
>,
R: CredentialReceipts<
'path,
'document,
Id = Q::Id,
Document = Q::Document,
Generation = Q::Generation,
>,
{
let selected = signer.signature();
let signature = selected.node;
let scope = timestamps.required().scope();
if selected.snapshot != query.snapshot()
|| counters.snapshot() != query.snapshot()
|| credentials.snapshot() != query.snapshot()
|| scope.location().snapshot != query.snapshot()
{
return Err(ParticipantError::Foreign { signature });
}
query.admit(signature, 1)?;
let entries = query.execution().vector();
for actual in [
counters.signature(),
credentials.signature(),
timestamps.required().signature(),
] {
let cost = signature
.hash_work()
.and_then(|cost| {
cost.checked_add(actual.hash_work()?)
.ok_or(MemoryError::WorkExceeded)
})
.map_err(|cause| ParticipantError::Resource {
node: signature,
cause,
})?;
query
.execution()
.charge_work(cost)
.map_err(|cause| ParticipantError::Resource {
node: signature,
cause,
})?;
if actual != signature {
return Err(ParticipantError::Foreign { signature });
}
}
let Some(path) = signer.path() else {
return Err(ParticipantError::UnprovenSigner);
};
if !timestamps
.required()
.includes_counters(counters, query)
.map_err(ParticipantError::Scope)?
{
return Err(ParticipantError::CounterTimestamps);
}
let mut collect = Collect { entries };
collect.push(
Participant::Signer(signature),
ParticipantAuthority::Path(path),
signature,
query,
)?;
counter_and_role_requirements(&mut collect, counters, credentials, timestamps, query)?;
for path in timestamps.paths() {
query.admit(signature, 2)?;
let source = path.actor().verified();
collect.push(
Participant::Timestamp {
property: source.property(),
token: source.token().node,
},
ParticipantAuthority::Path(path.path()),
source.property(),
query,
)?;
}
query.admit(signature, 1)?;
Ok(ParticipantRequirements {
scope,
signature,
entries: collect.entries.into_owned(),
origins: Origins {
signer: Origin::of(signer),
counters: Origin::of(counters),
credentials: Origin::of(credentials),
timestamps: Origin::of(timestamps),
},
issuer: PhantomData,
})
}