use crate::attributes::{self, AttributeXml};
use crate::binary::{self, BinaryXml, Location};
use crate::certificate::{self, CheckedPathCertificate};
use crate::hashes::ParticipantHashes;
use crate::header;
use crate::pki::CounterPaths;
use crate::prepare::Fields;
use crate::scalar::ScalarXml;
use crate::xml::{Access, AccessError, Xml};
use structured_asn1::{
Reader,
memory::{MemoryError, OwnedVec, Work},
};
use structured_ltv::baseline::BasicStage;
#[path = "hints.rs"]
mod hints;
#[derive(Clone, Copy)]
pub struct BasicInput<'receipt, 'prepared, 'path, 'document, N, D, G> {
pub fields: &'receipt Fields<'prepared, N, D, G>,
pub hashes: &'receipt ParticipantHashes<N, D, G>,
pub certificate: &'receipt CheckedPathCertificate<'path, 'document, N, D, G>,
pub counters: &'receipt CounterPaths<'path, 'document, N, D, G>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HintSource {
Protected(usize),
ValidationReference {
property: usize,
entry: usize,
},
}
pub struct Hint<N, D, G> {
location: Location<N, D, G>,
faults: OwnedVec<structured_cms::Error>,
source: HintSource,
}
impl<N: Copy, D: Copy, G: Copy> Hint<N, D, G> {
#[must_use]
pub fn source(&self) -> HintSource {
self.source
}
#[must_use]
pub fn location(&self) -> Location<N, D, G> {
self.location
}
#[must_use]
pub fn faults(&self) -> &[structured_cms::Error] {
&self.faults
}
}
pub struct Basic<'receipt, 'prepared, 'path, 'document, N, D, G> {
input: BasicInput<'receipt, 'prepared, 'path, 'document, N, D, G>,
included: Option<Location<N, D, G>>,
hints: OwnedVec<Hint<N, D, G>>,
hint_encodings_conform: bool,
}
impl<'r, 'p, 'v, 'd, N: Copy, D: Copy, G: Copy> Basic<'r, 'p, 'v, 'd, N, D, G> {
#[must_use]
pub fn inputs(&self) -> &BasicInput<'r, 'p, 'v, 'd, N, D, G> {
&self.input
}
#[must_use]
pub fn included(&self) -> Option<Location<N, D, G>> {
self.included
}
#[must_use]
pub fn hints(&self) -> &[Hint<N, D, G>] {
&self.hints
}
#[must_use]
pub fn all_root_requirements_met(&self) -> bool {
self.input.certificate.is_bound() && self.included.is_some() && self.hint_encodings_conform
}
}
impl<'v, 'd, N: Copy, D: Copy, G: Copy> BasicStage<'v, 'd> for Basic<'_, '_, 'v, 'd, N, D, G> {
type Id = N;
type Document = D;
type Generation = G;
type Signer = CheckedPathCertificate<'v, 'd, N, D, G>;
type Counters = CounterPaths<'v, 'd, N, D, G>;
fn signer(&self) -> &Self::Signer {
self.input.certificate
}
fn counters(&self) -> &Self::Counters {
self.input.counters
}
fn all_requirements_met(&self) -> bool {
self.all_root_requirements_met()
}
}
#[derive(Debug)]
pub enum BasicError<E, N, D, G> {
Foreign {
signature: N,
},
Access(AccessError<E, N>),
Attributes(attributes::Error<E, N>),
Inclusion(binary::Error<E, header::InclusionError, N, D, G>),
Hint(binary::Error<E, certificate::IssuerError<core::convert::Infallible>, N, D, G>),
Resource {
node: N,
cause: MemoryError,
},
}
pub type BasicResult<'receipt, 'prepared, 'path, 'document, X> = Result<
Basic<
'receipt,
'prepared,
'path,
'document,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
BasicError<<X as Xml>::Error, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
>;
type InputFor<'r, 'p, 'v, 'd, X> =
BasicInput<'r, 'p, 'v, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
type ProcessingResult<T, X> = Result<
T,
BasicError<<X as Xml>::Error, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
>;
pub fn basic<'r, 'p, 'v, 'd, X: BinaryXml + AttributeXml + ScalarXml>(
input: InputFor<'r, 'p, 'v, 'd, X>,
xml: &X,
operation: &Reader<'_>,
) -> BasicResult<'r, 'p, 'v, 'd, X> {
let prepared = input.fields.prepared();
let layout = prepared.bound().containers();
let signature = layout.signature();
let work = operation.execution();
let access = Access::new(xml, layout.snapshot(), &work);
require_sources(&input, &access)?;
attributes::check_counters(prepared, input.counters, &access)
.map_err(BasicError::Attributes)?;
let included = header::included(
xml,
prepared.header(),
input.certificate.selected().certificate(),
&work,
)
.map_err(BasicError::Inclusion)?;
let hints = hints::check_encodings(prepared, xml, operation)?;
access
.query(signature, |_, _, _| Ok(()))
.map_err(BasicError::Access)?;
Ok(Basic {
input,
included,
hints: hints.items,
hint_encodings_conform: hints.all_conformant,
})
}
fn require_sources<X: Xml, W: Work>(
input: &InputFor<'_, '_, '_, '_, X>,
access: &Access<'_, X, W>,
) -> ProcessingResult<(), X> {
let prepared = input.fields.prepared();
let layout = prepared.bound().containers();
let signature = layout.signature();
let foreign = || BasicError::Foreign { signature };
if input.hashes.snapshot() != access.snapshot()
|| input.counters.snapshot() != access.snapshot()
|| !core::ptr::eq(
input.certificate.selected().reference(),
prepared.certificates(),
)
{
return Err(foreign());
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(BasicError::Access)?;
let resource = |cause| BasicError::Resource {
node: signature,
cause,
};
if !access
.equal_id(signature, input.hashes.signature())
.map_err(resource)?
|| !access
.equal_id(signature, input.counters.signature())
.map_err(resource)?
|| input.hashes.counters().len() != input.counters.actors().len()
{
return Err(foreign());
}
for (hash, actor) in input.hashes.counters().iter().zip(input.counters.actors()) {
if !access
.equal_id(
hash.signature(),
actor.signer().counter().original().child(),
)
.map_err(resource)?
{
return Err(foreign());
}
}
Ok(())
}