use super::{Emit, Error, Work, Writer, XADES};
use crate::timestamp::XADES_141;
use structured_asn1::memory::{Execution, OwnedVec};
use structured_ltv::material::{ArtifactRef, Incorporation};
use structured_pkix::{
Certificate,
crl::CertificateList,
ocsp::{BasicResponse, ResponseBytes},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaterialKind {
Certificate,
Crl,
Ocsp,
}
enum Value<'a, 'source> {
Certificate(&'a Certificate<'source>),
Crl(&'a CertificateList<'source>),
Ocsp(&'a ResponseBytes<'source>),
}
pub struct Material<'a, 'source> {
value: Value<'a, 'source>,
}
impl<'a, 'source> Material<'a, 'source> {
#[must_use]
pub const fn certificate(value: &'a Certificate<'source>) -> Self {
Self {
value: Value::Certificate(value),
}
}
#[must_use]
pub const fn crl(value: &'a CertificateList<'source>) -> Self {
Self {
value: Value::Crl(value),
}
}
pub fn ocsp<E, W: Work>(
value: &'a ResponseBytes<'source>,
required: &BasicResponse<'_>,
work: &W,
) -> Result<Self, Error<E>> {
let original = required.signature_algorithm().execution_origin();
let envelope = value.execution_origin();
original
.charge_work_with(envelope, work, 20)
.map_err(Error::Resource)?;
if value.kind() != structured_pkix::oid::OCSP_BASIC {
return Err(Error::Value("ocsp.responseType"));
}
let bytes = value.payload();
let expected = required.as_bytes();
if bytes.len() != expected.len() {
return Err(Error::Value("ocsp.envelope"));
}
original
.charge_work_with(envelope, work, bytes.len() as u64)
.map_err(Error::Resource)?;
if bytes != expected {
return Err(Error::Value("ocsp.envelope"));
}
Ok(Self {
value: Value::Ocsp(value),
})
}
#[must_use]
pub const fn kind(&self) -> MaterialKind {
match self.value {
Value::Certificate(_) => MaterialKind::Certificate,
Value::Crl(_) => MaterialKind::Crl,
Value::Ocsp(_) => MaterialKind::Ocsp,
}
}
fn bytes(&self) -> &'source [u8] {
match self.value {
Value::Certificate(value) => value.as_bytes(),
Value::Crl(value) => value.as_bytes(),
Value::Ocsp(value) => value.as_bytes(),
}
}
fn execution_in<'q, W: Work>(&self, work: &'q W) -> Execution<'q>
where
'source: 'q,
{
match self.value {
Value::Certificate(value) => value.execution_in(work),
Value::Crl(value) => value.execution_in(work),
Value::Ocsp(value) => value.execution_in(work),
}
}
}
pub struct Materials<'a, 'source> {
entries: OwnedVec<Material<'a, 'source>>,
}
impl<'a, 'source> Materials<'a, 'source> {
pub fn missing<N: Copy, D: Copy, G: Copy, P: Copy, E, W: Work>(
required: &Incorporation<'a, 'source, N, D, G, P>,
work: &W,
mut envelope: impl FnMut(
&'a BasicResponse<'source>,
&Execution<'_>,
) -> Result<&'a ResponseBytes<'source>, E>,
) -> Result<Self, Error<E>> {
let mut entries = work.vector();
for entry in required.entries() {
let artifact = entry.artifact();
let scoped = artifact.execution_in(work);
scoped.charge_work(1).map_err(Error::Resource)?;
if entry.occurrence().is_some() {
continue;
}
let material = match artifact {
ArtifactRef::Certificate(value) => Material::certificate(value),
ArtifactRef::Crl(value) => Material::crl(value),
ArtifactRef::Ocsp(value) => {
let original = envelope(value, &scoped).map_err(Error::Writer)?;
Material::ocsp(original, value, &scoped)?
}
};
entries.try_push(material).map_err(Error::Resource)?;
}
Ok(Self {
entries: entries.into_owned(),
})
}
#[must_use]
pub fn entries(&self) -> &[Material<'a, 'source>] {
&self.entries
}
pub fn write<B: Writer, W: Work>(
&self,
writer: &mut B,
work: &W,
) -> Result<(), Error<B::Error>> {
write_material(&self.entries, writer, work)
}
}
pub fn write_material<B: Writer, W: Work>(
entries: &[Material<'_, '_>],
writer: &mut B,
work: &W,
) -> Result<(), Error<B::Error>> {
work.charge_work(1).map_err(Error::Resource)?;
if entries.is_empty() {
return Ok(());
}
let mut emit = Emit { writer, work };
emit.begin(XADES_141, "AnyValidationData")?;
let mut certificates = false;
for value in entries {
work.charge_work(1).map_err(Error::Resource)?;
if value.kind() == MaterialKind::Certificate {
certificates = true;
break;
}
}
if certificates {
emit.begin(XADES, "CertificateValues")?;
for value in entries {
work.charge_work(1).map_err(Error::Resource)?;
if value.kind() == MaterialKind::Certificate {
write_one(value, &mut emit)?;
}
}
emit.end()?;
}
let mut crls = false;
let mut ocsps = false;
for value in entries {
work.charge_work(1).map_err(Error::Resource)?;
match value.kind() {
MaterialKind::Crl => crls = true,
MaterialKind::Ocsp => ocsps = true,
MaterialKind::Certificate => {}
}
}
if crls || ocsps {
emit.begin(XADES, "RevocationValues")?;
for (kind, local) in [
(MaterialKind::Crl, "CRLValues"),
(MaterialKind::Ocsp, "OCSPValues"),
] {
if (kind == MaterialKind::Crl && !crls) || (kind == MaterialKind::Ocsp && !ocsps) {
continue;
}
emit.begin(XADES, local)?;
for value in entries {
work.charge_work(1).map_err(Error::Resource)?;
if value.kind() == kind {
write_one(value, &mut emit)?;
}
}
emit.end()?;
}
emit.end()?;
}
emit.end()
}
fn write_one<B: Writer, W: Work>(
value: &Material<'_, '_>,
emit: &mut Emit<'_, B, W>,
) -> Result<(), Error<B::Error>> {
let scoped = value.execution_in(emit.work);
let mut actual = Emit {
writer: emit.writer,
work: &scoped,
};
let local = match value.kind() {
MaterialKind::Certificate => "EncapsulatedX509Certificate",
MaterialKind::Crl => "EncapsulatedCRLValue",
MaterialKind::Ocsp => "EncapsulatedOCSPValue",
};
actual.binary(XADES, local, value.bytes())
}