use synta::{ObjectIdentifier, ToDer};
use crate::crypto::Pkcs12Encryptor;
use crate::pkcs12_types::{ID_CERT_BAG, ID_DATA, ID_PKCS8_SHROUDED_KEY_BAG, ID_X509_CERTIFICATE};
#[derive(Debug)]
pub enum Pkcs12BuilderError<E> {
NothingToPackage,
EncryptError(E),
EncodeError(String),
}
impl<E: std::fmt::Display> std::fmt::Display for Pkcs12BuilderError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NothingToPackage => {
f.write_str("PKCS#12 builder: no certificates or key to package")
}
Self::EncryptError(e) => write!(f, "PKCS#12 encryption error: {e}"),
Self::EncodeError(s) => write!(f, "PKCS#12 ASN.1 encoding error: {s}"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for Pkcs12BuilderError<E> {}
pub struct Pkcs12Builder {
certs: Vec<Vec<u8>>,
key: Option<Vec<u8>>,
}
impl Default for Pkcs12Builder {
fn default() -> Self {
Self::new()
}
}
impl Pkcs12Builder {
pub fn new() -> Self {
Self {
certs: Vec::new(),
key: None,
}
}
pub fn certificate(mut self, cert_der: &[u8]) -> Self {
self.certs.push(cert_der.to_vec());
self
}
pub fn private_key(mut self, key_der: &[u8]) -> Self {
self.key = Some(key_der.to_vec());
self
}
pub fn build<E: Pkcs12Encryptor>(
self,
password: &[u8],
encryptor: &E,
) -> Result<Vec<u8>, Pkcs12BuilderError<E::Error>> {
if self.certs.is_empty() && self.key.is_none() {
return Err(Pkcs12BuilderError::NothingToPackage);
}
let mut cert_bags_der = Vec::new();
for cert_der in &self.certs {
let bag = encode_cert_safe_bag(cert_der).map_err(Pkcs12BuilderError::EncodeError)?;
cert_bags_der.extend_from_slice(&bag);
}
let cert_safe_contents = der_wrap_sequence(cert_bags_der);
let cert_content_info = encode_id_data_content_info(&cert_safe_contents)
.map_err(Pkcs12BuilderError::EncodeError)?;
let key_content_info: Vec<u8> = if let Some(key_der) = &self.key {
let (alg_id_der, ciphertext) = encryptor
.encrypt(key_der, password)
.map_err(Pkcs12BuilderError::EncryptError)?;
let key_bag = encode_shrouded_key_safe_bag(&alg_id_der, &ciphertext)
.map_err(Pkcs12BuilderError::EncodeError)?;
let key_safe_contents = der_wrap_sequence(key_bag);
encode_id_data_content_info(&key_safe_contents)
.map_err(Pkcs12BuilderError::EncodeError)?
} else {
Vec::new()
};
let mut auth_safe_body = cert_content_info;
auth_safe_body.extend_from_slice(&key_content_info);
let auth_safe = der_wrap_sequence(auth_safe_body);
let auth_safe_content_info =
encode_id_data_content_info(&auth_safe).map_err(Pkcs12BuilderError::EncodeError)?;
let mac_data = encryptor
.compute_mac(&auth_safe, password)
.map_err(Pkcs12BuilderError::EncryptError)?;
let version_der: &[u8] = &[0x02, 0x01, 0x03]; let mut pfx_body = Vec::new();
pfx_body.extend_from_slice(version_der);
pfx_body.extend_from_slice(&auth_safe_content_info);
pfx_body.extend_from_slice(&mac_data);
Ok(der_wrap_sequence(pfx_body))
}
}
fn encode_cert_safe_bag(cert_der: &[u8]) -> Result<Vec<u8>, String> {
let x509_id = der_oid_tlv(ID_X509_CERTIFICATE)?;
let cert_value = der_explicit_ctx(0, &der_octet_string(cert_der));
let cert_bag = der_wrap_sequence([x509_id, cert_value].concat());
let bag_id = der_oid_tlv(ID_CERT_BAG)?;
let bag_value = der_explicit_ctx(0, &cert_bag);
Ok(der_wrap_sequence([bag_id, bag_value].concat()))
}
fn encode_shrouded_key_safe_bag(alg_id_der: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, String> {
let encrypted_data = der_octet_string(ciphertext);
let epki_body = [alg_id_der, encrypted_data.as_slice()].concat();
let epki = der_wrap_sequence(epki_body);
let bag_id = der_oid_tlv(ID_PKCS8_SHROUDED_KEY_BAG)?;
let bag_value = der_explicit_ctx(0, &epki);
Ok(der_wrap_sequence([bag_id, bag_value].concat()))
}
fn encode_id_data_content_info(content: &[u8]) -> Result<Vec<u8>, String> {
let oid_der = der_oid_tlv(ID_DATA)?;
let octet_str = der_octet_string(content);
let explicit_content = der_explicit_ctx(0, &octet_str);
Ok(der_wrap_sequence([oid_der, explicit_content].concat()))
}
pub(crate) fn der_push_len(out: &mut Vec<u8>, len: usize) {
if len < 0x80 {
out.push(len as u8);
} else if len < 0x100 {
out.extend_from_slice(&[0x81, len as u8]);
} else if len < 0x10000 {
out.extend_from_slice(&[0x82, (len >> 8) as u8, (len & 0xff) as u8]);
} else {
out.extend_from_slice(&[
0x83,
(len >> 16) as u8,
(len >> 8) as u8,
(len & 0xff) as u8,
]);
}
}
fn der_wrap(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(1 + 4 + body.len());
out.push(tag);
der_push_len(&mut out, body.len());
out.extend_from_slice(body);
out
}
pub(crate) fn der_wrap_sequence(body: Vec<u8>) -> Vec<u8> {
der_wrap(0x30, &body)
}
pub(crate) fn der_explicit_ctx(num: u8, content: &[u8]) -> Vec<u8> {
der_wrap(0xA0 | num, content)
}
pub(crate) fn der_octet_string(content: &[u8]) -> Vec<u8> {
der_wrap(0x04, content)
}
pub(crate) fn der_oid_tlv(components: &[u32]) -> Result<Vec<u8>, String> {
let oid = ObjectIdentifier::new(components)
.map_err(|e| format!("invalid OID {:?}: {e}", components))?;
oid.to_der()
.map_err(|e| format!("OID encode failed: {e:?}"))
}