mod checks;
pub use checks::Checks;
use crate::{
error::{Error as ArchiveError, resource},
uri::{self, Part},
zip::{Member, Names},
};
use structured_asn1::{memory::Work, range::RangeSource};
use structured_xades::{
binary::{self, OwnedBinaryXml},
certificate::{Agreement, DigestProvider},
digest,
reference::{LexicalSource, VisitError},
scalar::{self, UriValue, UriXml},
xml::{Access, AccessError, Name, Xml},
};
pub struct Catalogue<'v, 's, N, D, G, S: RangeSource> {
base: Member<'v, 's, S>,
reading: crate::manifest::Reading<N, D, G>,
signatures: structured_asn1::memory::OwnedVec<Target<'v, 's, S>>,
objects: structured_asn1::memory::OwnedVec<Target<'v, 's, S>>,
}
#[derive(Debug)]
pub enum ResolveError<E, N, SE> {
Archive(ArchiveError<SE>),
Reference(AccessError<VisitError<E, ArchiveError<SE>>, N>),
Storage {
node: N,
cause: structured_asn1::memory::MemoryError,
},
}
pub type ResolveResult<'v, 's, X, S> = Result<
Catalogue<'v, 's, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, S>,
ResolveError<<X as Xml>::Error, <X as Xml>::Id, <S as RangeSource>::Error>,
>;
type MemberResult<'v, 's, S> = crate::error::SourceResult<Option<Member<'v, 's, S>>, S>;
pub fn resolve_manifest<'v, 's, X: UriXml, S: RangeSource, W: Work>(
base: Member<'v, 's, S>,
reading: crate::manifest::Reading<X::Id, X::Document, X::Generation>,
xml: &X,
work: &W,
) -> ResolveResult<'v, 's, X, S> {
let execution = base.session().query(work);
base.archive()
.admit(&execution, base.central_span())
.map_err(ResolveError::Archive)?;
let access = Access::new(xml, reading.snapshot(), &execution);
access.inspect(reading.root(), |_| ()).map_err(|cause| {
ResolveError::Reference(match cause {
AccessError::Xml(cause) => AccessError::Xml(VisitError::Processing(cause)),
AccessError::Changed { node } => AccessError::Changed { node },
AccessError::Resource { node, cause } => AccessError::Resource { node, cause },
})
})?;
let mut signatures = execution.vector();
let mut objects = execution.vector();
signatures
.try_reserve_exact(reading.signatures().len())
.map_err(|cause| ResolveError::Storage {
node: reading.root(),
cause,
})?;
objects
.try_reserve_exact(reading.objects().len())
.map_err(|cause| ResolveError::Storage {
node: reading.root(),
cause,
})?;
for node in reading.signatures() {
let target = resolve(&base, &access, *node).map_err(ResolveError::Reference)?;
signatures
.try_push(target)
.map_err(|cause| ResolveError::Storage { node: *node, cause })?;
}
for object in reading.objects() {
let target = resolve(&base, &access, object.node).map_err(ResolveError::Reference)?;
objects
.try_push(target)
.map_err(|cause| ResolveError::Storage {
node: object.node,
cause,
})?;
}
base.archive()
.admit(&execution, base.central_span())
.map_err(ResolveError::Archive)?;
Ok(Catalogue {
base,
reading,
signatures: signatures.into_owned(),
objects: objects.into_owned(),
})
}
impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Catalogue<'v, 's, N, D, G, S> {
#[must_use]
pub fn base(&self) -> &Member<'v, 's, S> {
&self.base
}
#[must_use]
pub fn reading(&self) -> &crate::manifest::Reading<N, D, G> {
&self.reading
}
#[must_use]
pub fn signatures(&self) -> &[Target<'v, 's, S>] {
&self.signatures
}
#[must_use]
pub fn objects(&self) -> &[Target<'v, 's, S>] {
&self.objects
}
pub fn signature_member<W: Work>(&self, work: &W) -> MemberResult<'v, 's, S> {
self.base.archive().admit(work, self.base.central_span())?;
if self.signatures.len() != 1 {
return Ok(None);
}
unique(&self.signatures[0], work)
}
pub fn object_member<W: Work>(&self, ordinal: usize, work: &W) -> MemberResult<'v, 's, S> {
self.base.archive().admit(work, self.base.central_span())?;
let Some(target) = self.objects.get(ordinal) else {
return Ok(None);
};
unique(target, work)
}
pub fn group<W: Work>(
&self,
work: &W,
) -> crate::zip::Acquisition<Option<crate::evidence::Group<'v, 's, S>>, S> {
let archive = self.base.archive();
let execution = self.base.session().query(work);
let fail = |cause| crate::zip::ReadFailure::Processing {
member: self.base.id(),
cause,
};
archive
.admit(&execution, self.base.central_span())
.map_err(fail)?;
if self.objects.is_empty() {
return Ok(None);
}
let mut members = execution.vector();
for target in &self.objects {
archive
.admit(&execution, self.base.central_span())
.map_err(fail)?;
let Some(member) = unique(target, &execution).map_err(fail)? else {
return Ok(None);
};
members
.try_push(member.id())
.map_err(|cause| fail(resource(self.base.central_span(), cause)))?;
}
crate::evidence::Group::new(archive, members.into_owned(), &execution).map(Some)
}
}
fn unique<'v, 's, S: RangeSource, W: Work>(
target: &Target<'v, 's, S>,
work: &W,
) -> Result<Option<Member<'v, 's, S>>, ArchiveError<S::Error>> {
if let Target::Members {
names,
part: Part::Whole,
} = target
&& names.len() == 1
{
names.member(0, work)
} else {
Ok(None)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Unchecked {
NotExecuted,
Input,
Method,
Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DigestReading {
Unchecked(Unchecked),
Checked(Agreement),
}
pub struct ObjectCheck<'v, 's, N, D, G, S: RangeSource> {
snapshot: structured_xades::xml::Snapshot<D, G>,
root: N,
object: crate::manifest::Object<N>,
target: Target<'v, 's, S>,
digest: DigestReading,
}
impl<'v, 's, N: Copy, D: Copy, G: Copy, S: RangeSource> ObjectCheck<'v, 's, N, D, G, S> {
#[must_use]
pub fn snapshot(&self) -> structured_xades::xml::Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn root(&self) -> N {
self.root
}
#[must_use]
pub fn object(&self) -> crate::manifest::Object<N> {
self.object
}
#[must_use]
pub fn target(&self) -> &Target<'v, 's, S> {
&self.target
}
#[must_use]
pub fn digest(&self) -> DigestReading {
self.digest
}
}
#[derive(Debug)]
pub enum CheckError<E, PE, N, D, G, SE, SD, SG> {
Reference(AccessError<VisitError<E, ArchiveError<SE>>, N>),
Input(crate::zip::ReadFailure<SE, SD, SG>),
Digest(ObjectDigestError<E, PE, N, D, G, SE, SD, SG>),
}
pub type ObjectDigestError<E, PE, N, D, G, SE, SD, SG> =
binary::Error<E, digest::Error<crate::zip::ReadFailure<SE, SD, SG>, PE>, N, D, G>;
pub type CheckResult<'v, 's, X, S, PE> = Result<
Option<ObjectCheck<'v, 's, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, S>>,
CheckError<
<X as Xml>::Error,
PE,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
<S as RangeSource>::Error,
<S as RangeSource>::Document,
<S as RangeSource>::Generation,
>,
>;
pub async fn check_object<'p, 'v, 's, X, S, P, W>(
base: &Member<'v, 's, S>,
reading: &crate::manifest::Reading<X::Id, X::Document, X::Generation>,
ordinal: usize,
xml: &X,
provider: &'p P,
work: &W,
) -> CheckResult<'v, 's, X, S, P::Error>
where
X: UriXml + OwnedBinaryXml,
S: RangeSource,
P: DigestProvider<'p, X>,
W: Work,
{
let access = Access::new(xml, reading.snapshot(), work);
access.inspect(reading.root(), |_| ()).map_err(|cause| {
CheckError::Reference(match cause {
AccessError::Xml(cause) => AccessError::Xml(VisitError::Processing(cause)),
AccessError::Resource { node, cause } => AccessError::Resource { node, cause },
AccessError::Changed { node } => AccessError::Changed { node },
})
})?;
let Some(object) = reading.objects().get(ordinal).copied() else {
return Ok(None);
};
let target = resolve(base, &access, object.node).map_err(CheckError::Reference)?;
let answer = match (object.method, object.value) {
(crate::manifest::Slot::One(method), crate::manifest::Slot::One(value)) => {
if let Target::Members {
names,
part: Part::Whole,
} = &target
&& names.len() == 1
{
let member = names
.member(0, work)
.map_err(|cause| {
CheckError::Reference(AccessError::Xml(VisitError::Visitor(cause)))
})?
.expect("the immutable exact candidate range contains one original member");
let mut source = member.source(work).map_err(CheckError::Input)?;
DigestReading::Checked(
digest::check_xml_async(
xml,
digest::Reference {
snapshot: reading.snapshot(),
method,
value,
},
&mut source,
provider,
work,
)
.await
.map_err(CheckError::Digest)?,
)
} else {
DigestReading::Unchecked(Unchecked::Input)
}
}
(crate::manifest::Slot::One(_), _) => DigestReading::Unchecked(Unchecked::Value),
_ => DigestReading::Unchecked(Unchecked::Method),
};
base.archive()
.admit(work, base.central_span())
.map_err(|cause| CheckError::Reference(AccessError::Xml(VisitError::Visitor(cause))))?;
Ok(Some(ObjectCheck {
snapshot: reading.snapshot(),
root: reading.root(),
object,
target,
digest: answer,
}))
}
pub enum Target<'v, 's, S: RangeSource> {
Absent,
Malformed,
Unsupported,
SameDocument(Part),
Root(Part),
Members {
names: Names<'v, 's, S>,
part: Part,
},
Negative(uri::Negative),
}
pub type ErrorOf<X, S> = AccessError<
VisitError<<X as Xml>::Error, ArchiveError<<S as RangeSource>::Error>>,
<X as Xml>::Id,
>;
pub fn resolve<'v, 's, X: UriXml, S: RangeSource, W: Work>(
base: &Member<'v, 's, S>,
access: &Access<'_, X, W>,
node: X::Id,
) -> Result<Target<'v, 's, S>, ErrorOf<X, S>> {
scalar::with_uri(
access,
node,
LexicalSource::Attribute(Name {
namespace: "",
local: "URI",
}),
|value| {
let work = access.execution();
let archive = base.archive();
archive.admit(work, base.central_span())?;
let target = match value {
UriValue::Absent => Target::Absent,
UriValue::ComplexContent | UriValue::Malformed => Target::Malformed,
UriValue::Unsupported => Target::Unsupported,
UriValue::Valid(value) => match uri::resolve(base.name(), value, work)
.map_err(|cause| resource(base.central_span(), cause))?
{
uri::Target::SameDocument(part) => Target::SameDocument(part),
uri::Target::Root(part) => Target::Root(part),
uri::Target::Negative(cause) => Target::Negative(cause),
uri::Target::Member { name, part } => Target::Members {
names: archive.find(name.as_ref(), work)?,
part,
},
},
};
archive.admit(work, base.central_span())?;
Ok(target)
},
)
}