use crate::{
Error,
source::{Node, Value},
};
use structured_asn1::{
Oid, Reader,
memory::{OwnedVec, Work},
};
use structured_cms::{attribute::Placement, signer::SignerInfo};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
ArchiveV1,
ArchiveV2,
LongTermValidation,
References,
SignatureReferences,
}
impl Kind {
pub(crate) fn from_oid(value: Oid<'_>) -> Option<Self> {
let bytes = value.as_bytes();
if bytes.len() == 11 {
if bytes[..10] != [42, 134, 72, 134, 247, 13, 1, 9, 16, 2] {
return None;
}
match bytes[10] {
27 => Some(Self::ArchiveV1),
48 => Some(Self::ArchiveV2),
26 => Some(Self::References),
25 => Some(Self::SignatureReferences),
_ => None,
}
} else if bytes == [4, 0, 0x8d, 0x45, 2, 2] {
Some(Self::LongTermValidation)
} else {
None
}
}
pub(crate) fn is_reference_timestamp(self) -> bool {
matches!(self, Self::References | Self::SignatureReferences)
}
pub(crate) fn is_archive(self) -> bool {
matches!(self, Self::ArchiveV1 | Self::ArchiveV2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Required<'document> {
pub signature: Node,
pub property: Node,
pub kind: Kind,
pub placement: Placement,
pub value: Option<Value<'document>>,
}
pub(crate) fn roots<'d, W: Work>(
message: &structured_cms::SignedData<'d>,
parent: &Reader<'d>,
work: &W,
) -> crate::Result<OwnedVec<Required<'d>, W>> {
let mut out = work.vector();
for signer in message.signers() {
parent
.charge(1)
.map_err(|cause| Error::encoding(cause, "cades.preservation"))?;
collect(signer, parent, &mut out)?;
}
Ok(out)
}
pub(crate) fn collect<'d, W: Work>(
signer: &SignerInfo<'d>,
parent: &Reader<'d>,
out: &mut OwnedVec<Required<'d>, W>,
) -> crate::Result<()> {
signer.require_session(parent, "cades.preservation")?;
for (placement, set) in [
(Placement::Signed, signer.signed_attributes()),
(Placement::Unsigned, signer.unsigned_attributes()),
] {
let Some(set) = set else {
continue;
};
for attribute in set.items() {
parent
.charge(5 * (1 + attribute.kind().as_bytes().len().min(11) as u64))
.map_err(|cause| Error::encoding(cause, "cades.preservation"))?;
let kind = Kind::from_oid(attribute.kind());
let Some(kind) = kind else {
continue;
};
let resource =
|cause| Error::resource(cause, "cades.preservation", attribute.span().start);
if attribute.values().is_empty() {
out.try_push(Required {
signature: Node::of(signer.span()),
property: Node::of(attribute.span()),
kind,
placement,
value: None,
})
.map_err(resource)?;
}
for (ordinal, value) in attribute.values().iter().enumerate() {
let reader = attribute
.value_reader_in(ordinal, parent, "cades.preservation")?
.ok_or(Error::Foreign {
at: value.span().start,
})?;
out.try_push(Required {
signature: Node::of(signer.span()),
property: Node::of(attribute.span()),
kind,
placement,
value: Some(Value::of(&reader, *value, "cades.preservation")),
})
.map_err(resource)?;
}
}
}
Ok(())
}