use super::{ReferenceIdentifier, ReferenceProperty, ResponderIdentifier};
use crate::{
binary::{self, BinaryXml, Location},
xml::Access,
};
use structured_asn1::{Reader, Tag};
use structured_pkix::{
Certificate,
algorithm::digest::AsnDigest,
ocsp::{ResponderId, ResponderMatch},
};
#[derive(Debug)]
pub enum ResponderKeyError<E> {
Index {
index: usize,
},
Encoding(structured_asn1::Error),
Responder(structured_pkix::Error),
Trailing {
offset: u64,
},
Callback(E),
}
pub type ReferenceKeyResult<X, E> = binary::BinaryResult<
Option<binary::LocatedValue<ResponderMatch, X>>,
X,
ResponderKeyError<structured_pkix::error::CallbackError<E>>,
>;
pub fn with_responder_key<X: BinaryXml, R, E>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(&'source [u8]) -> Result<R, E>,
) -> binary::BinaryResult<Option<binary::LocatedValue<R, X>>, X, ResponderKeyError<E>> {
with_key_at(xml, references, index, operation, |hash, _, _| {
visitor(hash)
})
}
pub fn matches_reference_key<'k, X: BinaryXml, D, DE>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
certificate: &Certificate<'_>,
digest: &'k D,
operation: &Reader<'_>,
) -> ReferenceKeyResult<X, DE>
where
D: AsnDigest<'k, Error = DE>,
{
with_key_at(xml, references, index, operation, |hash, reader, at| {
ResponderId::KeyHash(hash).matches_certificate(
certificate,
digest,
&reader.execution(),
"xades.ocspIdentifier.byKey",
at,
)
})
}
fn with_key_at<X: BinaryXml, R, E>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(&'source [u8], &Reader<'source>, u64) -> Result<R, E>,
) -> binary::BinaryResult<Option<binary::LocatedValue<R, X>>, X, ResponderKeyError<E>> {
let location = Location {
snapshot: references.snapshot(),
node: references.node(),
};
let work = operation.execution();
Access::new(xml, references.snapshot(), &work)
.query(references.node(), |_, _, _| Ok(()))
.map_err(|cause| binary::Error::Access { location, cause })?;
let Some(entry) = references.entries().get(index) else {
return Err(binary::Error::Callback {
location,
cause: ResponderKeyError::Index { index },
});
};
let ReferenceIdentifier::Ocsp {
responder: ResponderIdentifier::Key(node),
..
} = entry.identifier()
else {
return Ok(None);
};
let location = Location {
snapshot: references.snapshot(),
node,
};
binary::with_der_reader(xml, location, operation, |reader| {
let element = reader
.expect(Tag::context(true, 2))
.map_err(ResponderKeyError::Encoding)?;
let responder =
ResponderId::read_element(reader, &element).map_err(ResponderKeyError::Responder)?;
if !reader.is_empty() {
return Err(ResponderKeyError::Trailing {
offset: reader.offset(),
});
}
let ResponderId::KeyHash(hash) = responder else {
unreachable!("checked byKey tag produced a name");
};
visitor(hash, reader, element.span().start)
.map(|value| Some(binary::Located { location, value }))
.map_err(ResponderKeyError::Callback)
})
}