use synta::{Encoding, Integer, ObjectIdentifier, OctetStringRef, RawDer, SetOf, ToDer};
use crate::cms_rfc5652_types::{EncryptedContentInfo, EnvelopedData, OriginatorInfo};
#[derive(Debug)]
pub enum EnvelopedDataBuilderError {
NoRecipients,
Encode(synta::Error),
InvalidOid(&'static str),
}
impl std::fmt::Display for EnvelopedDataBuilderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoRecipients => f.write_str("EnvelopedData requires at least one RecipientInfo"),
Self::Encode(e) => write!(f, "DER encoding error: {e:?}"),
Self::InvalidOid(s) => write!(f, "invalid OID constant: {s}"),
}
}
}
impl std::error::Error for EnvelopedDataBuilderError {}
impl From<synta::Error> for EnvelopedDataBuilderError {
fn from(e: synta::Error) -> Self {
Self::Encode(e)
}
}
pub struct EnvelopedDataBuilder {
enc_alg_id_der: Vec<u8>,
ciphertext: Vec<u8>,
recipient_info_ders: Vec<Vec<u8>>,
originator_certs: Vec<Vec<u8>>,
originator_crls: Vec<Vec<u8>>,
unprotected_attrs: Option<Vec<u8>>,
}
impl EnvelopedDataBuilder {
pub fn new(enc_alg_id_der: Vec<u8>, ciphertext: Vec<u8>) -> Self {
Self {
enc_alg_id_der,
ciphertext,
recipient_info_ders: Vec::new(),
originator_certs: Vec::new(),
originator_crls: Vec::new(),
unprotected_attrs: None,
}
}
pub fn add_recipient_info(mut self, info_der: Vec<u8>) -> Self {
self.recipient_info_ders.push(info_der);
self
}
pub fn originator_cert(mut self, cert_der: &[u8]) -> Self {
self.originator_certs.push(cert_der.to_vec());
self
}
pub fn originator_crl(mut self, crl_der: &[u8]) -> Self {
self.originator_crls.push(crl_der.to_vec());
self
}
pub fn unprotected_attrs(mut self, attrs: &[u8]) -> Self {
self.unprotected_attrs = Some(attrs.to_vec());
self
}
pub fn build(self) -> Result<Vec<u8>, EnvelopedDataBuilderError> {
if self.recipient_info_ders.is_empty() {
return Err(EnvelopedDataBuilderError::NoRecipients);
}
let ri_raw: Vec<RawDer<'_>> = self
.recipient_info_ders
.iter()
.map(|b| RawDer(b.as_slice()))
.collect();
let ri_set_der = SetOf::from_vec(ri_raw).to_der()?;
let certs_content: Vec<u8> = self
.originator_certs
.iter()
.flat_map(|c| c.iter().copied())
.collect();
let crls_content: Vec<u8> = self
.originator_crls
.iter()
.flat_map(|c| c.iter().copied())
.collect();
let origin_info: Option<OriginatorInfo<'_>> =
if certs_content.is_empty() && crls_content.is_empty() {
None
} else {
Some(OriginatorInfo {
certs: if certs_content.is_empty() {
None
} else {
Some(RawDer(&certs_content))
},
crls: if crls_content.is_empty() {
None
} else {
Some(RawDer(&crls_content))
},
})
};
let content_type = ObjectIdentifier::new(crate::pkcs7_types::ID_DATA)
.map_err(|_| EnvelopedDataBuilderError::InvalidOid("id-data"))?;
let content_encryption_algorithm: crate::AlgorithmIdentifier<'_> =
synta::Decoder::new(&self.enc_alg_id_der, Encoding::Der).decode()?;
let eci = EncryptedContentInfo {
content_type,
content_encryption_algorithm,
encrypted_content: Some(OctetStringRef::new(&self.ciphertext)),
};
Ok(EnvelopedData {
version: Integer::from_i64(0),
originator_info: origin_info,
recipient_infos: RawDer(&ri_set_der),
encrypted_content_info: eci,
unprotected_attrs: self.unprotected_attrs.as_deref().map(RawDer),
}
.to_der()?)
}
}