use crate::binding::Bound;
use crate::container::XADES;
use crate::material::{self, Catalogue};
use crate::properties;
use crate::timestamp::{self, InputError, XADES_141};
use crate::xml::{Access, Name, Snapshot, Xml};
use structured_asn1::memory::Work;
mod refs;
pub(crate) use refs::read as read_references;
mod ref_catalogue;
mod ref_digest;
pub use ref_catalogue::{
MaterialDigest, ReferenceCatalogueResult, ReferenceDigests, ReferenceRow, ReferenceSearch,
compare_reference_catalogues, compare_reference_catalogues_with_tokens,
};
mod ref_issuer;
mod ref_time;
pub use ref_time::{ReferenceTime, ReferenceTimeError, matches_reference_time};
mod ref_envelope;
mod ref_key;
mod ref_name;
mod ref_ocsp;
pub use ref_envelope::{
OcspEnvelopeError, OcspEnvelopeReference, OcspEnvelopeResult, OcspReferenceInput,
check_ocsp_envelope_reference,
};
pub use ref_issuer::with_reference_issuer_serial;
pub use ref_key::{
ReferenceKeyResult, ResponderKeyError, matches_reference_key, with_responder_key,
};
pub use ref_name::{ReferenceNameError, matches_reference_name};
pub use ref_ocsp::{
OcspReferenceError, OcspReferenceIdentity, OcspReferenceResult, check_ocsp_reference,
};
mod ref_credential_certificates;
mod ref_crl;
mod ref_crl_value;
mod ref_path;
mod ref_presence;
mod ref_roles;
mod ref_status_required;
pub use ref_credential_certificates::{
CredentialCertificate, CredentialCertificateReferences, CredentialCertificateResult,
CredentialCertificateRow, CredentialReferencePlace, check_credential_certificate_references,
};
mod ref_status_roles;
pub use ref_path::{
RootPathReferenceResult, RootPathReferences, RootReferenceRow,
check_root_certificate_references,
};
pub use ref_presence::{
ReferencePresence, ReferencePresenceError, ReferencePresenceFailure, ReferencePresenceResult,
ReferencePresenceRow, reference_presence,
};
pub use ref_roles::{
CertificateRoleComparison, CertificateRoleError, CertificateRoleResult, CertificateRoleRow,
CertificateRoles, check_certificate_reference_roles,
};
pub use ref_status_required::{CredentialStatusSource, PathStatusSource};
pub use ref_status_roles::{
RevocationRoleResult, RevocationRoles, StatusRoleAgreement, StatusRoleComparison,
StatusRoleRow, StatusSourceComparison, check_revocation_reference_roles,
};
mod ref_fields;
pub use ref_crl::{
CrlReferenceError, CrlReferenceIdentity, CrlReferenceResult, check_crl_reference,
};
pub use ref_crl_value::{
CrlValueError, CrlValueReference, CrlValueResult, check_crl_value_reference,
};
pub use ref_digest::{
ReferenceDigest, ReferenceDigestError, ReferenceTarget, compare_reference_digest,
compare_reference_target,
};
pub use ref_fields::{ReferenceFieldError, ReferenceFieldResult, check_reference_fields};
pub use refs::{
ReferenceEntry, ReferenceField, ReferenceIdentifier, ReferenceKind, ReferenceProperty,
ResponderIdentifier,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Role {
Any,
Timestamp,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Hint<N> {
Adjacent(N),
Unresolved {
uri_present: bool,
},
}
#[derive(Debug)]
pub enum Error<E, N> {
Input(InputError<E, N>),
Property(properties::Error<E, N>),
ForbiddenUri {
node: N,
},
Empty {
node: N,
},
}
#[derive(Debug)]
pub struct Data<N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
node: N,
role: Role,
hint: Option<Hint<N>>,
certificates: Option<Catalogue<N, D, G>>,
revocations: Option<Catalogue<N, D, G>>,
}
impl<N: Copy, D: Copy, G: Copy> Data<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 node(&self) -> N {
self.node
}
#[must_use]
pub fn role(&self) -> Role {
self.role
}
#[must_use]
pub fn hint(&self) -> Option<Hint<N>> {
self.hint
}
#[must_use]
pub fn certificates(&self) -> Option<&Catalogue<N, D, G>> {
self.certificates.as_ref()
}
#[must_use]
pub fn revocations(&self) -> Option<&Catalogue<N, D, G>> {
self.revocations.as_ref()
}
}
pub type Reading<X> = Data<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
pub type ValidationResult<T, X> = Result<T, Error<<X as Xml>::Error, <X as Xml>::Id>>;
pub fn read<X: Xml, W: Work>(
access: &Access<'_, X, W>,
bound: &Bound<X::Id, X::Document, X::Generation>,
node: X::Id,
) -> ValidationResult<Reading<X>, X> {
let parent = timestamp::unsigned_parent(access, bound, node).map_err(Error::Input)?;
let mut previous = None;
let mut found = false;
for child in access.children(parent) {
let child = child.map_err(|cause| Error::Input(InputError::Child(cause)))?;
if access
.equal_id(child, node)
.map_err(|cause| Error::Input(InputError::Resource { node: child, cause }))?
{
found = true;
break;
}
previous = Some(child);
}
if !found {
return Err(Error::Input(InputError::Foreign { node }));
}
read_at(access, bound.containers().signature(), node, previous)
}
pub(crate) fn read_at<X: Xml, W: Work>(
access: &Access<'_, X, W>,
signature: X::Id,
node: X::Id,
previous: Option<X::Id>,
) -> ValidationResult<Reading<X>, X> {
let role = if named(access, node, XADES_141, "AnyValidationData")? {
Role::Any
} else if named(access, node, XADES_141, "TimeStampValidationData")? {
Role::Timestamp
} else {
return Err(Error::Input(InputError::Unexpected { node }));
};
let uri_present = access
.attribute(
node,
Name {
namespace: "",
local: "URI",
},
|value| value.is_some(),
)
.map_err(|cause| Error::Input(InputError::Access(cause)))?;
if role == Role::Any && uri_present {
return Err(Error::ForbiddenUri { node });
}
let mut hint = None;
if role == Role::Timestamp {
hint = Some(Hint::Unresolved { uri_present });
if let Some(prior) = previous
&& (named(access, prior, XADES, "SignatureTimeStamp")?
|| named(access, prior, XADES_141, "ArchiveTimeStamp")?
|| named(access, prior, XADES_141, "SigAndRefsTimeStampV2")?
|| named(access, prior, XADES_141, "RefsOnlyTimeStampV2")?)
{
hint = Some(Hint::Adjacent(prior));
}
}
let mut certificates = None;
let mut revocations = None;
for child in access.children(node) {
let child = child.map_err(|cause| Error::Input(InputError::Child(cause)))?;
if named(access, child, XADES, "CertificateValues")? {
if certificates.is_some() {
return Err(Error::Property(properties::Error::Duplicate {
node: child,
}));
}
if revocations.is_some() {
return Err(Error::Property(properties::Error::Order { node: child }));
}
certificates = Some(material::read(access, child).map_err(Error::Property)?);
} else if named(access, child, XADES, "RevocationValues")? {
if revocations.is_some() {
return Err(Error::Property(properties::Error::Duplicate {
node: child,
}));
}
revocations = Some(material::read(access, child).map_err(Error::Property)?);
} else {
return Err(Error::Input(InputError::Unexpected { node: child }));
}
}
if role == Role::Any
&& certificates.as_ref().is_none_or(|c| c.entries().is_empty())
&& revocations.as_ref().is_none_or(|c| c.entries().is_empty())
{
return Err(Error::Empty { node });
}
Ok(Data {
snapshot: access.snapshot(),
signature,
node,
role,
hint,
certificates,
revocations,
})
}
fn named<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
namespace: &str,
local: &str,
) -> ValidationResult<bool, X> {
access
.has_name(node, Name { namespace, local })
.map_err(|cause| Error::Input(InputError::Access(cause)))
}