use super::{ReferenceKind, ReferenceProperty};
use crate::{
binary::{self, BinaryXml, Location},
certificate::Agreement,
digest::{self, StreamingDigest},
material,
xml::Access,
};
use structured_asn1::memory::Work;
use structured_cms::stream::RangesError;
#[derive(Clone, Copy)]
pub enum ReferenceTarget<'view, 'document, N, D, G> {
Certificate(&'view structured_pkix::Certificate<'document>),
Crl(&'view structured_pkix::crl::CertificateList<'document>),
Ocsp(&'view structured_pkix::ocsp::ResponseBytes<'document>),
Encapsulated(&'view material::Source<N, D, G>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReferenceDigest {
Compared(Agreement),
DifferentKind,
IdentifierOnly,
OtherFormat,
}
#[derive(Debug)]
pub enum ReferenceDigestError<PE> {
UnboundPath,
Index {
index: usize,
},
Foreign,
Digest(digest::Error<RangesError, PE>),
}
pub fn compare_reference_digest<X: BinaryXml, P: StreamingDigest<X>, W: Work>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
source: &material::Material<X>,
provider: &P,
execution: &W,
) -> binary::BinaryResult<ReferenceDigest, X, ReferenceDigestError<P::Error>> {
compare_reference_target(
xml,
references,
index,
ReferenceTarget::Encapsulated(source),
provider,
execution,
)
}
pub fn compare_reference_target<X: BinaryXml, P: StreamingDigest<X>, W: Work>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
target: ReferenceTarget<'_, '_, X::Id, X::Document, X::Generation>,
provider: &P,
execution: &W,
) -> binary::BinaryResult<ReferenceDigest, X, ReferenceDigestError<P::Error>> {
let location = Location {
snapshot: references.snapshot(),
node: references.node(),
};
let access = Access::new(xml, references.snapshot(), execution);
access
.query(references.node(), |_, _, _| Ok(()))
.map_err(|cause| binary::Error::Access { location, cause })?;
let (kind, bytes) = match target {
ReferenceTarget::Certificate(value) => (material::Kind::Certificate, value.as_bytes()),
ReferenceTarget::Crl(value) => (material::Kind::Crl, value.as_bytes()),
ReferenceTarget::Ocsp(value) => (material::Kind::Ocsp, value.as_bytes()),
ReferenceTarget::Encapsulated(source) => {
if source.location().snapshot != references.snapshot() {
return Err(binary::Error::Callback {
location,
cause: ReferenceDigestError::Foreign,
});
}
(source.kind(), source.bytes())
}
};
let Some(entry) = references.entries().get(index) else {
return Err(binary::Error::Callback {
location,
cause: ReferenceDigestError::Index { index },
});
};
let expected_kind = match entry.kind() {
ReferenceKind::Certificate => material::Kind::Certificate,
ReferenceKind::Crl => material::Kind::Crl,
ReferenceKind::Ocsp => material::Kind::Ocsp,
ReferenceKind::Other => return Ok(ReferenceDigest::OtherFormat),
};
if kind != expected_kind {
return Ok(ReferenceDigest::DifferentKind);
}
let Some((method, value)) = entry.digest() else {
return Ok(ReferenceDigest::IdentifierOnly);
};
digest::check(
xml,
digest::Reference {
snapshot: references.snapshot(),
method,
value,
},
&mut [bytes],
provider,
execution,
)
.map(ReferenceDigest::Compared)
.map_err(|cause| cause.map_callback(ReferenceDigestError::Digest))
}