use crate::binary::{self, Located, LocatedValue, Location, OwnedBinaryXml, OwnedDecoded};
use crate::container::{DS, XADES};
use crate::header;
use crate::xml::{Access, AccessError, Xml};
use structured_asn1::{Encoding, Reader};
use structured_pkix::{Certificate, crl::CertificateList, ocsp::Response};
mod catalogue;
mod checks;
pub use checks::{CheckError, CheckResult, Checks, check_participants};
mod conditional;
pub use conditional::{
ConditionalError, ConditionalFailure, ConditionalFinding, ConditionalPlacement,
ConditionalResult, conditional_placement,
};
mod counters;
pub mod credentials;
pub use catalogue::{Catalogue, Entry, read};
mod pipeline;
pub use pipeline::{
Basic, Error as PipelineError, Opaque, PipelineResult, Placement, Retained, View,
with_counter_values, with_values,
};
pub(crate) mod exclusivity;
#[cfg(test)]
mod fixture;
mod incorporation;
mod index;
pub use incorporation::{
IncorporatedArtifact, Incorporation, IncorporationError, IncorporationResult, incorporation,
};
mod participants;
mod timestamp_placement;
pub use timestamp_placement::{
TimestampPlacement, TimestampPlacementError, TimestampPlacementFailure,
TimestampPlacementFinding, TimestampPlacementResult, timestamp_placement,
};
pub mod status;
mod uses;
pub use exclusivity::{
ExclusiveFinding, ExclusiveFindingKind, ExclusivePlacement, ExclusivityError,
ExclusivityResult, exclusivity,
};
pub use index::{
ArchiveError, Archived, ArchivedCertificate, ArtifactRef, ArtifactSource, Index, IndexError,
Occurrence, Occurrences, PathCertificate, PathCertificates,
};
pub use participants::{
AnchorMaterial, ParticipantArchiveError, ParticipantArchiveResult, ParticipantMaterial,
archived_participants,
};
pub use uses::{ArtifactUse, ArtifactUses, ArtifactUsesResult, artifact_uses};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Kind {
Certificate,
Crl,
Ocsp,
}
impl Kind {
fn local(self) -> &'static str {
match self {
Self::Certificate => "EncapsulatedX509Certificate",
Self::Crl => "EncapsulatedCRLValue",
Self::Ocsp => "EncapsulatedOCSPValue",
}
}
}
#[derive(Debug)]
pub struct Source<N, D, G> {
kind: Kind,
original: Located<OwnedDecoded, N, D, G>,
}
pub type Material<X> = Source<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
#[derive(Debug)]
pub enum RetainError<E, N> {
HeaderIndex {
signature: N,
index: usize,
},
Access(AccessError<E, N>),
Name {
node: N,
kind: Kind,
},
}
pub type RetainResult<T, X> = Result<T, RetainError<<X as Xml>::Error, <X as Xml>::Id>>;
#[derive(Debug)]
#[expect(
clippy::large_enum_variant,
reason = "fixed-size parsed value avoids an extra allocation per material; any retained collection admits its full enum capacity"
)]
pub enum Value<'source> {
Certificate(Certificate<'source>),
Crl(CertificateList<'source>),
Ocsp(Response<'source>),
}
#[derive(Debug)]
pub enum Cause {
Encoding(structured_asn1::Error),
Pki(structured_pkix::Error),
Empty {
offset: u64,
},
Trailing {
offset: u64,
},
}
pub type ReadResult<'source, N, D, G> =
Result<Located<Value<'source>, N, D, G>, Located<Cause, N, D, G>>;
impl<N: Copy, D: Copy, G: Copy> Source<N, D, G> {
#[must_use]
pub fn kind(&self) -> Kind {
self.kind
}
#[must_use]
pub fn location(&self) -> Location<N, D, G> {
self.original.location
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
&self.original.value.bytes
}
pub fn read_in<'source>(
&'source self,
operation: &Reader<'source>,
) -> ReadResult<'source, N, D, G> {
let read = || {
let mut reader = self
.original
.value
.reader_in(operation, Encoding::Der)
.map_err(Cause::Encoding)?;
let element = reader
.next_element()
.map_err(Cause::Encoding)?
.ok_or(Cause::Empty {
offset: reader.offset(),
})?;
let value = match self.kind {
Kind::Certificate => Value::Certificate(
Certificate::read_element(&reader, &element).map_err(Cause::Pki)?,
),
Kind::Crl => Value::Crl(
CertificateList::read_element(&reader, &element).map_err(Cause::Pki)?,
),
Kind::Ocsp => {
Value::Ocsp(Response::read_element(&reader, &element).map_err(Cause::Pki)?)
}
};
if !reader.is_empty() {
return Err(Cause::Trailing {
offset: reader.offset(),
});
}
Ok(value)
};
match read() {
Ok(value) => Ok(Located {
location: self.location(),
value,
}),
Err(value) => Err(Located {
location: self.location(),
value,
}),
}
}
}
pub fn retain<X: OwnedBinaryXml>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
kind: Kind,
operation: &Reader<'_>,
) -> RetainResult<Material<X>, X> {
retain_expected(xml, location, kind, XADES, kind.local(), operation)
}
pub fn retain_header<X: OwnedBinaryXml>(
xml: &X,
header: &header::Reading<X>,
index: usize,
operation: &Reader<'_>,
) -> RetainResult<Material<X>, X> {
let execution = operation.execution();
let access = Access::new(xml, header.snapshot(), &execution);
access
.query(header.signature(), |_, _, _| Ok(()))
.map_err(RetainError::Access)?;
let Some(&node) = header.certificates().get(index) else {
return Err(RetainError::HeaderIndex {
signature: header.signature(),
index,
});
};
retain_expected(
xml,
Location {
snapshot: header.snapshot(),
node,
},
Kind::Certificate,
DS,
"X509Certificate",
operation,
)
}
fn retain_expected<X: OwnedBinaryXml>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
kind: Kind,
namespace: &str,
local: &str,
operation: &Reader<'_>,
) -> RetainResult<Material<X>, X> {
let execution = operation.execution();
let access = Access::new(xml, location.snapshot, &execution);
if !access
.has_name(location.node, crate::xml::Name { namespace, local })
.map_err(RetainError::Access)?
{
return Err(RetainError::Name {
node: location.node,
kind,
});
}
let original: LocatedValue<OwnedDecoded, X> =
binary::owned_source(xml, location, operation).map_err(RetainError::Access)?;
Ok(Source { kind, original })
}