use crate::crypto::Pkcs12Encryptor;
use crate::pkcs12_types::{
MacData, MacDigestInfo, Pbes2Params, Pbkdf2Params, ID_AES128_CBC, ID_AES192_CBC, ID_AES256_CBC,
ID_HMAC_WITH_SHA256, ID_HMAC_WITH_SHA384, ID_HMAC_WITH_SHA512, ID_PBES2, ID_PBKDF2,
};
use super::cms::{build_alg_id_der, OpensslEncryptorError};
use native_ossl::cipher::CipherAlg;
use native_ossl::digest::DigestAlg;
use native_ossl::kdf::Pbkdf2Builder;
use native_ossl::mac::HmacCtx;
use native_ossl::rand::Rand;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pkcs12Cipher {
Aes128Cbc,
Aes192Cbc,
Aes256Cbc,
}
impl Pkcs12Cipher {
fn alg_name(self) -> &'static std::ffi::CStr {
match self {
Self::Aes128Cbc => c"AES-128-CBC",
Self::Aes192Cbc => c"AES-192-CBC",
Self::Aes256Cbc => c"AES-256-CBC",
}
}
fn key_len(self) -> usize {
match self {
Self::Aes128Cbc => 16,
Self::Aes192Cbc => 24,
Self::Aes256Cbc => 32,
}
}
fn oid(self) -> &'static [u32] {
match self {
Self::Aes128Cbc => ID_AES128_CBC,
Self::Aes192Cbc => ID_AES192_CBC,
Self::Aes256Cbc => ID_AES256_CBC,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pkcs12HmacAlgorithm {
Sha256,
Sha384,
Sha512,
}
impl Pkcs12HmacAlgorithm {
fn alg_name(self) -> &'static std::ffi::CStr {
match self {
Self::Sha256 => c"SHA2-256",
Self::Sha384 => c"SHA2-384",
Self::Sha512 => c"SHA2-512",
}
}
fn output_len(self) -> usize {
match self {
Self::Sha256 => 32,
Self::Sha384 => 48,
Self::Sha512 => 64,
}
}
fn oid(self) -> &'static [u32] {
match self {
Self::Sha256 => ID_HMAC_WITH_SHA256,
Self::Sha384 => ID_HMAC_WITH_SHA384,
Self::Sha512 => ID_HMAC_WITH_SHA512,
}
}
fn digest_oid(self) -> &'static [u32] {
match self {
Self::Sha256 => crate::ID_SHA256,
Self::Sha384 => crate::ID_SHA384,
Self::Sha512 => crate::ID_SHA512,
}
}
}
#[derive(Debug, Clone)]
pub struct Pkcs12Config {
pub encryption_iterations: u32,
pub mac_iterations: u32,
pub cipher: Pkcs12Cipher,
pub encryption_prf: Pkcs12HmacAlgorithm,
pub mac_algorithm: Pkcs12HmacAlgorithm,
}
impl Default for Pkcs12Config {
fn default() -> Self {
Self {
encryption_iterations: 600_000,
mac_iterations: 600_000,
cipher: Pkcs12Cipher::Aes256Cbc,
encryption_prf: Pkcs12HmacAlgorithm::Sha256,
mac_algorithm: Pkcs12HmacAlgorithm::Sha256,
}
}
}
pub struct OpensslPkcs12Encryptor {
config: Pkcs12Config,
}
impl OpensslPkcs12Encryptor {
pub fn new() -> Self {
Self {
config: Pkcs12Config::default(),
}
}
pub fn with_config(config: Pkcs12Config) -> Self {
Self { config }
}
}
impl Default for OpensslPkcs12Encryptor {
fn default() -> Self {
Self::new()
}
}
impl Pkcs12Encryptor for OpensslPkcs12Encryptor {
type Error = OpensslEncryptorError;
fn encrypt(
&self,
plaintext: &[u8],
password: &[u8],
) -> Result<(Vec<u8>, Vec<u8>), OpensslEncryptorError> {
let cipher = CipherAlg::fetch(self.config.cipher.alg_name(), None)?;
let key_len = self.config.cipher.key_len();
let iv_len = cipher.iv_len();
let kdf_salt = Rand::bytes(16)?;
let iv = Rand::bytes(iv_len)?;
let prf_md = DigestAlg::fetch(self.config.encryption_prf.alg_name(), None)?;
let key = Pbkdf2Builder::new(&prf_md, password, &kdf_salt)
.iterations(self.config.encryption_iterations)
.derive_to_vec(key_len)?;
let mut ctx = cipher.encrypt(&key, &iv, None)?;
let block = cipher.block_size();
let mut ciphertext = vec![0u8; plaintext.len() + block];
let n = ctx.update(plaintext, &mut ciphertext)?;
let m = ctx.finalize(&mut ciphertext[n..])?;
ciphertext.truncate(n + m);
let alg_id_der = build_pbes2_alg_id_der(
&kdf_salt,
&iv,
self.config.encryption_iterations,
key_len as u32,
self.config.cipher.oid(),
self.config.encryption_prf.oid(),
)?;
Ok((alg_id_der, ciphertext))
}
fn compute_mac(
&self,
auth_safe_content: &[u8],
password: &[u8],
) -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::AlgorithmIdentifier;
use synta::{Element, Null, ObjectIdentifier, OctetStringRef};
let mac_alg = self.config.mac_algorithm;
let mac_key_len = mac_alg.output_len();
let mac_salt = Rand::bytes(8)?;
let mac_md = DigestAlg::fetch(mac_alg.alg_name(), None)?;
let mac_key = Pbkdf2Builder::new(&mac_md, password, &mac_salt)
.iterations(self.config.mac_iterations)
.derive_to_vec(mac_key_len)?;
let mut hmac_ctx = HmacCtx::new(&mac_md, &mac_key)?;
hmac_ctx.update(auth_safe_content)?;
let hmac_bytes = hmac_ctx.finish_to_vec()?;
let hmac_oid = ObjectIdentifier::new(mac_alg.digest_oid()).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm(
"invalid SHA digest OID components in Pkcs12HmacAlgorithm".into(),
)
})?;
let dig_alg = AlgorithmIdentifier {
algorithm: hmac_oid,
parameters: Some(Element::Null(Null)),
};
let mac_digest_info = MacDigestInfo {
digest_algorithm: dig_alg,
digest: OctetStringRef::new(&hmac_bytes),
};
let mac_data = MacData {
mac: mac_digest_info,
mac_salt: OctetStringRef::new(&mac_salt),
iterations: synta::Integer::from(self.config.mac_iterations),
};
Ok(mac_data.to_der()?)
}
}
fn build_pbes2_alg_id_der(
kdf_salt: &[u8],
iv: &[u8],
iterations: u32,
key_len: u32,
cipher_oid: &[u32],
prf_oid: &[u32],
) -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::AlgorithmIdentifier;
use synta::traits::Decode;
use synta::{Element, Null, ObjectIdentifier, OctetStringRef, RawDer};
let prf_alg_der = {
let oid = ObjectIdentifier::new(prf_oid).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid PRF OID components".into())
})?;
AlgorithmIdentifier {
algorithm: oid,
parameters: Some(Element::Null(Null)),
}
.to_der()?
};
let pbkdf2_params_der = Pbkdf2Params {
salt: OctetStringRef::new(kdf_salt),
iteration_count: synta::Integer::from(iterations),
key_length: Some(synta::Integer::from(key_len)),
prf: Some(RawDer(&prf_alg_der)),
}
.to_der()?;
let kdf_alg_der = {
let oid = ObjectIdentifier::new(ID_PBKDF2).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid id-PBKDF2 OID".into())
})?;
let mut dec = synta::Decoder::new(&pbkdf2_params_der, synta::Encoding::Der);
let params_elem: Element<'_> = Element::decode(&mut dec)?;
AlgorithmIdentifier {
algorithm: oid,
parameters: Some(params_elem),
}
.to_der()?
};
let enc_alg_der = build_alg_id_der(cipher_oid, iv)?;
let pbes2_params_der = Pbes2Params {
key_derivation_func: RawDer(&kdf_alg_der),
encryption_scheme: RawDer(&enc_alg_der),
}
.to_der()?;
let oid = ObjectIdentifier::new(ID_PBES2)
.map_err(|_| OpensslEncryptorError::UnsupportedAlgorithm("invalid id-PBES2 OID".into()))?;
let mut dec = synta::Decoder::new(&pbes2_params_der, synta::Encoding::Der);
let params_elem: Element<'_> = Element::decode(&mut dec)?;
Ok(AlgorithmIdentifier {
algorithm: oid,
parameters: Some(params_elem),
}
.to_der()?)
}