use crate::binding::Bound;
use crate::container::{DS, XADES};
use crate::xml::{Access, AccessError, ChildError, Name, Node, Snapshot, Xml};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
mod claimed_time;
pub use claimed_time::{
CalendarFact, CalendarValue, CalendarXml, ClaimedTime, ClaimedTimeBinding, ClaimedTimeError,
ClaimedTimeFailure, ClaimedTimeResult, DateTimeFact, DateTimeXml, check_time,
};
const SIGNATURE_PROPERTIES: &[&str] = &[
"SigningTime",
"SigningCertificateV2",
"SignaturePolicyIdentifier",
"SignatureProductionPlaceV2",
"SignerRoleV2",
];
const LEGACY_PROPERTIES: &[&str] = &[
"SigningCertificate",
"SignatureProductionPlace",
"SignerRole",
];
const OBJECT_PROPERTIES: &[&str] = &[
"DataObjectFormat",
"CommitmentTypeIndication",
"AllDataObjectsTimeStamp",
"IndividualDataObjectsTimeStamp",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataObjectRole {
Format,
Commitment,
AllTimestamp,
IndividualTimestamp,
Extension,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DataObject<N> {
pub node: N,
pub role: DataObjectRole,
}
#[derive(Debug)]
pub struct DataObjects<N> {
parent: N,
entries: OwnedVec<DataObject<N>>,
}
impl<N: Copy> DataObjects<N> {
#[must_use]
pub fn parent(&self) -> N {
self.parent
}
#[must_use]
pub fn entries(&self) -> &[DataObject<N>] {
&self.entries
}
}
#[derive(Debug)]
pub enum Error<E, N> {
Selection {
parent: N,
index: usize,
},
Lexical {
node: N,
local: &'static str,
},
Access(AccessError<E, N>),
Child(ChildError<E, N>),
Missing {
parent: N,
local: &'static str,
},
Duplicate {
node: N,
},
Order {
node: N,
},
Forbidden {
node: N,
},
Unexpected {
node: N,
},
Resource {
node: N,
cause: MemoryError,
},
}
pub struct Signed<N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
parent: N,
time: N,
certificate: N,
policy: Option<N>,
place: Option<N>,
role: Option<N>,
extensions: OwnedVec<N>,
objects: Option<DataObjects<N>>,
}
impl<N: Copy, D: Copy, G: Copy> Signed<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 parent(&self) -> N {
self.parent
}
#[must_use]
pub fn signing_time(&self) -> N {
self.time
}
#[must_use]
pub fn signing_certificate(&self) -> N {
self.certificate
}
#[must_use]
pub fn policy(&self) -> Option<N> {
self.policy
}
#[must_use]
pub fn place(&self) -> Option<N> {
self.place
}
#[must_use]
pub fn role(&self) -> Option<N> {
self.role
}
#[must_use]
pub fn extensions(&self) -> &[N] {
&self.extensions
}
#[must_use]
pub fn data_objects(&self) -> Option<&DataObjects<N>> {
self.objects.as_ref()
}
}
pub type Reading<X> = Signed<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
pub type PropertyResult<T, X> = Result<T, Error<<X as Xml>::Error, <X as Xml>::Id>>;
pub(crate) fn simple_content<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
) -> PropertyResult<(), X> {
let mut child = access.first_child(parent).map_err(Error::Access)?;
while let Some(node) = child {
if access
.inspect(node, |value| matches!(value, Node::Element(_)))
.map_err(Error::Access)?
{
return Err(Error::Unexpected { node });
}
child = access.next_sibling(parent, node).map_err(Error::Access)?;
}
Ok(())
}
pub(crate) fn digest_value<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
) -> PropertyResult<(X::Id, X::Id), X> {
let mut children = access.children(parent);
let method = children
.next()
.transpose()
.map_err(Error::Child)?
.ok_or(Error::Missing {
parent,
local: "DigestMethod",
})?;
if !access
.has_name(
method,
Name {
namespace: DS,
local: "DigestMethod",
},
)
.map_err(Error::Access)?
{
return Err(Error::Unexpected { node: method });
}
let value = children
.next()
.transpose()
.map_err(Error::Child)?
.ok_or(Error::Missing {
parent,
local: "DigestValue",
})?;
if !access
.has_name(
value,
Name {
namespace: DS,
local: "DigestValue",
},
)
.map_err(Error::Access)?
{
return Err(Error::Unexpected { node: value });
}
if let Some(extra) = children.next() {
return Err(Error::Unexpected {
node: extra.map_err(Error::Child)?,
});
}
simple_content(access, value)?;
Ok((method, value))
}
pub(crate) type Sections<X> = (Option<<X as Xml>::Id>, Option<<X as Xml>::Id>);
pub(crate) fn sections<X: Xml, W: Work>(
access: &Access<'_, X, W>,
layout: &crate::container::Layout<X>,
) -> PropertyResult<Sections<X>, X> {
if layout.snapshot() != access.snapshot() {
return Err(Error::Access(AccessError::Changed {
node: layout.signed(),
}));
}
let mut parent = None;
let mut objects = None;
for child in access.children(layout.signed()) {
let node = child.map_err(Error::Child)?;
if named(access, node, "SignedSignatureProperties")? {
if parent.is_some() {
return Err(Error::Duplicate { node });
}
if objects.is_some() {
return Err(Error::Order { node });
}
parent = Some(node);
} else if named(access, node, "SignedDataObjectProperties")? {
if objects.is_some() {
return Err(Error::Duplicate { node });
}
objects = Some(node);
} else {
return Err(Error::Unexpected { node });
}
}
Ok((parent, objects))
}
pub fn read<X: Xml, W: Work>(
access: &Access<'_, X, W>,
bound: &Bound<X::Id, X::Document, X::Generation>,
) -> PropertyResult<Reading<X>, X> {
let layout = bound.containers();
let (parent, objects) = sections(access, layout)?;
let parent = parent.ok_or(Error::Missing {
parent: layout.signed(),
local: "SignedSignatureProperties",
})?;
let mut fields = [None; 5];
let mut previous = 0;
let mut extensions = access
.query(parent, |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
for child in access.children(parent) {
let node = child.map_err(Error::Child)?;
let kind = classify(access, node, SIGNATURE_PROPERTIES, LEGACY_PROPERTIES)?;
let rank = match kind {
Kind::Known(rank) => {
if fields[rank].is_some() {
return Err(Error::Duplicate { node });
}
fields[rank] = Some(node);
rank
}
Kind::Extension => {
extensions
.try_push(node)
.map_err(|cause| Error::Resource { node, cause })?;
SIGNATURE_PROPERTIES.len()
}
Kind::Forbidden => return Err(Error::Forbidden { node }),
Kind::Unexpected => return Err(Error::Unexpected { node }),
};
if rank < previous {
return Err(Error::Order { node });
}
previous = rank;
}
let time = fields[0].ok_or(Error::Missing {
parent,
local: "SigningTime",
})?;
let certificate = fields[1].ok_or(Error::Missing {
parent,
local: "SigningCertificateV2",
})?;
simple_content(access, time)?;
if let Some(place) = fields[3] {
production_place(access, place)?;
}
let objects = objects
.map(|parent| read_objects(access, parent))
.transpose()?;
Ok(Signed {
snapshot: layout.snapshot(),
signature: layout.signature(),
parent,
time,
certificate,
policy: fields[2],
place: fields[3],
role: fields[4],
extensions: extensions.into_owned(),
objects,
})
}
fn production_place<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
) -> PropertyResult<(), X> {
const FIELDS: &[&str] = &[
"City",
"StreetAddress",
"StateOrProvince",
"PostalCode",
"CountryName",
];
let mut seen = [false; 5];
let mut previous = None;
for child in access.children(parent) {
let node = child.map_err(Error::Child)?;
let rank = match classify(access, node, FIELDS, &[])? {
Kind::Known(rank) => rank,
Kind::Forbidden | Kind::Unexpected | Kind::Extension => {
return Err(Error::Unexpected { node });
}
};
if seen[rank] {
return Err(Error::Duplicate { node });
}
if let Some(previous) = previous
&& rank < previous
{
return Err(Error::Order { node });
}
seen[rank] = true;
previous = Some(rank);
simple_content(access, node)?;
}
if previous.is_none() {
return Err(Error::Missing {
parent,
local: "production-place field",
});
}
Ok(())
}
enum Kind {
Known(usize),
Extension,
Forbidden,
Unexpected,
}
fn classify<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
known: &[&str],
forbidden: &[&str],
) -> PropertyResult<Kind, X> {
access
.inspect(node, |value| {
let Node::Element(actual) = value else {
return Ok(Kind::Unexpected);
};
if actual.namespace.is_empty() {
return Ok(Kind::Unexpected);
}
if !access.equal(actual.namespace, XADES)? {
return Ok(Kind::Extension);
}
for (rank, local) in known.iter().enumerate() {
if access.equal(actual.local, local)? {
return Ok(Kind::Known(rank));
}
}
for local in forbidden {
if access.equal(actual.local, local)? {
return Ok(Kind::Forbidden);
}
}
Ok(Kind::Unexpected)
})
.map_err(Error::Access)?
.map_err(|cause| Error::Resource { node, cause })
}
pub(crate) fn read_objects<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
) -> PropertyResult<DataObjects<X::Id>, X> {
let mut entries = access
.query(parent, |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
let mut previous = 0;
for child in access.children(parent) {
let node = child.map_err(Error::Child)?;
let (rank, role) = match classify(access, node, OBJECT_PROPERTIES, &[])? {
Kind::Known(rank) => {
let roles = [
DataObjectRole::Format,
DataObjectRole::Commitment,
DataObjectRole::AllTimestamp,
DataObjectRole::IndividualTimestamp,
];
debug_assert!(rank < roles.len());
(rank, roles[rank])
}
Kind::Extension => (OBJECT_PROPERTIES.len(), DataObjectRole::Extension),
Kind::Forbidden | Kind::Unexpected => return Err(Error::Unexpected { node }),
};
if rank < previous {
return Err(Error::Order { node });
}
entries
.try_push(DataObject { node, role })
.map_err(|cause| Error::Resource { node, cause })?;
previous = rank;
}
Ok(DataObjects {
parent,
entries: entries.into_owned(),
})
}
fn named<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
local: &str,
) -> PropertyResult<bool, X> {
access
.has_name(
node,
Name {
namespace: XADES,
local,
},
)
.map_err(Error::Access)
}