use super::{DS, Datatype, Emit, Error, Event, Value, Work, Writer, XADES};
use structured_pkix::Certificate;
#[derive(Debug, Clone, Copy)]
pub struct DataObject<'a> {
pub reference: &'a str,
pub mime_type: &'a str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReferenceKind {
SignedProperties,
CounterSignature,
}
impl ReferenceKind {
#[must_use]
pub const fn type_uri(self) -> &'static str {
match self {
Self::SignedProperties => crate::binding::SIGNED_PROPERTIES,
Self::CounterSignature => "http://uri.etsi.org/01903#CountersignedSignature",
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BasicReferences<'a> {
pub signed_properties: &'a str,
pub counter_signature: Option<&'a str>,
}
pub struct Basic<'a, 'certificate> {
pub target: &'a str,
pub signed_properties_id: &'a str,
pub signing_time: &'a str,
pub certificate: &'a Certificate<'certificate>,
pub certificate_digest_method: &'a str,
pub certificate_digest: &'a [u8],
pub objects: &'a [DataObject<'a>],
pub counter_signature: Option<&'a str>,
}
impl Basic<'_, '_> {
pub fn write<'a, B: Writer, W: Work>(
&'a self,
writer: &mut B,
work: &W,
) -> Result<BasicReferences<'a>, Error<B::Error>> {
work.charge_work(1).map_err(Error::Resource)?;
if self.target.is_empty()
|| self.signed_properties_id.is_empty()
|| self.signing_time.is_empty()
|| self.certificate_digest_method.is_empty()
|| self.certificate_digest.is_empty()
{
return Err(Error::Value("basic.required"));
}
if self.counter_signature.is_none() && self.objects.is_empty() {
return Err(Error::Value("basic.dataObjects"));
}
for object in self.objects {
work.charge_work(1).map_err(Error::Resource)?;
if object.reference.is_empty() {
return Err(Error::Value("dataObject.reference"));
}
let mut mime = structured_cms::mime::Syntax::<true>::new();
if !mime.feed(object.mime_type, work).map_err(Error::Resource)? || !mime.complete() {
return Err(Error::Value("dataObject.mimeType"));
}
}
let mut emit = Emit { writer, work };
emit.begin(XADES, "QualifyingProperties")?;
emit.attribute("Target", self.target)?;
emit.begin(XADES, "SignedProperties")?;
emit.attribute("Id", self.signed_properties_id)?;
emit.begin(XADES, "SignedSignatureProperties")?;
emit.begin(XADES, "SigningTime")?;
emit.event(Event::Text(Value {
lexical: self.signing_time,
datatype: Datatype::ZonedDateTime,
}))?;
emit.end()?;
emit.begin(XADES, "SigningCertificateV2")?;
emit.begin(XADES, "Cert")?;
emit.begin(XADES, "CertDigest")?;
emit.digest(self.certificate_digest_method, self.certificate_digest)?;
emit.end()?;
emit.end()?;
emit.end()?;
emit.end()?;
if !self.objects.is_empty() {
emit.begin(XADES, "SignedDataObjectProperties")?;
for object in self.objects {
emit.begin(XADES, "DataObjectFormat")?;
emit.attribute("ObjectReference", object.reference)?;
emit.text(XADES, "MimeType", object.mime_type)?;
emit.end()?;
}
emit.end()?;
}
emit.end()?;
emit.end()?;
Ok(BasicReferences {
signed_properties: self.signed_properties_id,
counter_signature: self.counter_signature,
})
}
pub fn write_key_info<B: Writer, W: Work>(
&self,
writer: &mut B,
work: &W,
) -> Result<(), Error<B::Error>> {
let scoped = self.certificate.execution_in(work);
let mut emit = Emit {
writer,
work: &scoped,
};
emit.begin(DS, "KeyInfo")?;
emit.begin(DS, "X509Data")?;
emit.binary(DS, "X509Certificate", self.certificate.as_bytes())?;
emit.end()?;
emit.end()
}
}