use structured_asn1::memory::{MemoryError, OwnedVec, Work};
use structured_xades::{
attributes::AttributeXml,
reference::VisitError,
scalar::ScalarXml,
xml::{Access, AccessError, Name, Node, Snapshot, Xml},
};
mod fields;
pub const NAMESPACE: &str = "http://uri.etsi.org/02918/v1.2.1#";
pub(crate) const DS: &str = "http://www.w3.org/2000/09/xmldsig#";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Slot<N> {
Absent,
One(N),
Ambiguous,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Boolean {
Absent,
Value(bool),
Malformed,
}
#[derive(Debug, Clone, Copy)]
pub struct Object<N> {
pub node: N,
pub method: Slot<N>,
pub value: Slot<N>,
pub rootfile: Boolean,
}
#[derive(Debug, Clone, Copy)]
pub struct Extension<N> {
pub owner: N,
pub node: N,
pub critical: Boolean,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FaultKind {
Text,
Unexpected,
Missing,
Duplicate,
Order,
Scalar,
InstanceUnassessed,
}
#[derive(Debug, Clone, Copy)]
pub struct Fault<N> {
pub node: N,
pub kind: FaultKind,
}
#[derive(Debug)]
pub enum Error<E, N> {
Access(AccessError<E, N>),
Attributes(AccessError<VisitError<E, MemoryError>, N>),
Resource {
node: N,
cause: MemoryError,
},
Root(N),
}
type ResultOf<T, X> = Result<T, Error<<X as Xml>::Error, <X as Xml>::Id>>;
pub type InspectionResult<X> =
ResultOf<Reading<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>, X>;
pub struct Reading<N, D, G> {
snapshot: Snapshot<D, G>,
root: N,
signatures: OwnedVec<N>,
objects: OwnedVec<Object<N>>,
extensions: OwnedVec<Extension<N>>,
faults: OwnedVec<Fault<N>>,
}
pub(crate) struct Builder<N, W: Work> {
signatures: OwnedVec<N, W>,
objects: OwnedVec<Object<N>, W>,
extensions: OwnedVec<Extension<N>, W>,
faults: OwnedVec<Fault<N>, W>,
}
impl<N: Copy, W: Work> Builder<N, W> {
fn fault<E>(&mut self, node: N, kind: FaultKind) -> Result<(), Error<E, N>> {
self.faults
.try_push(Fault { node, kind })
.map_err(|cause| Error::Resource { node, cause })
}
}
impl<N: Copy, D: Copy + Eq, G: Copy + Eq> Reading<N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn root(&self) -> N {
self.root
}
#[must_use]
pub fn objects(&self) -> &[Object<N>] {
&self.objects
}
#[must_use]
pub fn signatures(&self) -> &[N] {
&self.signatures
}
#[must_use]
pub fn extensions(&self) -> &[Extension<N>] {
&self.extensions
}
#[must_use]
pub fn faults(&self) -> &[Fault<N>] {
&self.faults
}
}
pub fn inspect<X: AttributeXml + ScalarXml, W: Work>(
access: &Access<'_, X, W>,
root: X::Id,
) -> InspectionResult<X> {
if !access
.has_name(root, name("ASiCManifest"))
.map_err(Error::Access)?
{
return Err(Error::Root(root));
}
let work = access.execution();
let mut output = Builder {
signatures: work.vector(),
objects: work.vector(),
extensions: work.vector(),
faults: work.vector(),
};
fields::attributes(access, root, &[], &mut output)?;
let mut stage = 0;
let mut extension = false;
walk(access, root, |node, element| {
if !element {
return output.fault(node, FaultKind::Text);
}
if access
.has_name(node, name("SigReference"))
.map_err(Error::Access)?
{
if stage != 0 {
output.fault(node, FaultKind::Order)?;
}
if !output.signatures.is_empty() {
output.fault(node, FaultKind::Duplicate)?;
}
fields::attributes(access, node, &["URI", "MimeType"], &mut output)?;
fields::uri(access, node, &mut output)?;
fields::empty(access, node, &mut output)?;
output
.signatures
.try_push(node)
.map_err(|cause| Error::Resource { node, cause })?;
} else if access
.has_name(node, name("DataObjectReference"))
.map_err(Error::Access)?
{
if stage > 1 || output.signatures.is_empty() {
output.fault(node, FaultKind::Order)?;
}
stage = stage.max(1);
let value = fields::object(access, node, &mut output)?;
output
.objects
.try_push(value)
.map_err(|cause| Error::Resource { node, cause })?;
} else if access
.has_name(node, name("ASiCManifestExtensions"))
.map_err(Error::Access)?
{
if extension {
output.fault(node, FaultKind::Duplicate)?;
}
if output.objects.is_empty() {
output.fault(node, FaultKind::Order)?;
}
stage = 2;
extension = true;
fields::extensions(access, root, node, &mut output)?;
} else {
output.fault(node, FaultKind::Unexpected)?;
}
Ok(())
})?;
if output.signatures.is_empty() || output.objects.is_empty() {
output.fault(root, FaultKind::Missing)?;
}
Ok(Reading {
snapshot: access.snapshot(),
root,
signatures: output.signatures.into_owned(),
objects: output.objects.into_owned(),
extensions: output.extensions.into_owned(),
faults: output.faults.into_owned(),
})
}
fn name(local: &'static str) -> Name<'static> {
Name {
namespace: NAMESPACE,
local,
}
}
fn walk<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
mut visit: impl FnMut(X::Id, bool) -> ResultOf<(), X>,
) -> ResultOf<(), X> {
let mut child = access.first_child(parent).map_err(Error::Access)?;
while let Some(node) = child {
let class = access
.inspect(node, |read| match read {
Node::Element(_) => Ok(Some(true)),
Node::Text(text) => {
access.execution().charge_work(text.len() as u64)?;
for byte in text.bytes() {
if !matches!(byte, b' ' | b'\r' | b'\n' | b'\t') {
return Ok(Some(false));
}
}
Ok(None)
}
Node::Other => Ok(None),
})
.map_err(Error::Access)?
.map_err(|cause| Error::Resource { node, cause })?;
if let Some(element) = class {
visit(node, element)?;
}
child = access.next_sibling(parent, node).map_err(Error::Access)?;
}
Ok(())
}