use crate::core::{ErrorCode, Result};
use std::sync::Arc;
use zeroize::Zeroize;
#[cfg(feature = "as2")]
use crate::as2::{As2PreparedSendCredentials, As2SendCredentials, As2SendPolicy};
#[cfg(feature = "as4")]
use crate::as4::{As4PreparedSendCredentials, As4SendCredentials, As4SendPolicy};
#[derive(Clone, Default)]
pub struct PartnerCredentials {
pub signing_cert_pem: Option<Arc<[u8]>>,
pub signing_key_pem: Option<Vec<u8>>,
pub recipient_cert_pem: Option<Arc<[u8]>>,
}
impl std::fmt::Debug for PartnerCredentials {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PartnerCredentials")
.field("signing_cert_pem", &self.signing_cert_pem)
.field(
"signing_key_pem",
&crate::core::redact_present(self.signing_key_pem.is_some()),
)
.field("recipient_cert_pem", &self.recipient_cert_pem)
.finish()
}
}
impl Drop for PartnerCredentials {
fn drop(&mut self) {
if let Some(key) = self.signing_key_pem.as_mut() {
key.zeroize();
}
}
}
impl PartnerCredentials {
#[cfg(feature = "as2")]
pub fn to_as2_send_credentials(&self) -> As2SendCredentials {
As2SendCredentials {
signing_cert_pem: self.signing_cert_pem.clone(),
signing_key_pem: self.signing_key_pem.clone(),
recipient_cert_pem: self.recipient_cert_pem.clone(),
}
}
#[cfg(feature = "as4")]
pub fn to_as4_send_credentials(&self) -> As4SendCredentials {
As4SendCredentials {
signing_cert_pem: self.signing_cert_pem.clone(),
signing_key_pem: self.signing_key_pem.clone(),
recipient_cert_pem: self.recipient_cert_pem.clone(),
}
}
#[cfg(feature = "as2")]
pub fn prepare_as2_for_policy(
&self,
policy: &As2SendPolicy,
stage: &'static str,
error_code: ErrorCode,
) -> Result<As2PreparedSendCredentials> {
self.to_as2_send_credentials()
.prepare_for_policy(policy, stage, error_code)
}
#[cfg(feature = "as4")]
pub fn prepare_as4_for_policy(
&self,
policy: &As4SendPolicy,
stage: &'static str,
error_code: ErrorCode,
) -> Result<As4PreparedSendCredentials> {
self.to_as4_send_credentials()
.prepare_for_policy(policy, stage, error_code)
}
#[cfg(any(feature = "as2", feature = "as4"))]
pub fn from_pkcs12(der: &[u8], passphrase: &str) -> crate::core::Result<Self> {
use crate::core::{AsxError, ErrorContext};
use openssl::pkcs12::Pkcs12;
let pkcs12 = Pkcs12::from_der(der).map_err(|e| {
AsxError::new(
ErrorCode::InvalidInput,
format!("PKCS#12 DER parsing failed: {e}"),
ErrorContext::new("credentials_from_pkcs12"),
)
})?;
let parsed = pkcs12.parse2(passphrase).map_err(|e| {
AsxError::new(
ErrorCode::InvalidInput,
format!("PKCS#12 passphrase or structure error: {e}"),
ErrorContext::new("credentials_from_pkcs12"),
)
})?;
let signing_cert_pem = parsed
.cert
.as_ref()
.map(|cert| {
cert.to_pem()
.map_err(|e| {
AsxError::new(
ErrorCode::InvalidInput,
format!("PKCS#12 certificate to PEM conversion failed: {e}"),
ErrorContext::new("credentials_from_pkcs12"),
)
})
.map(Arc::from)
})
.transpose()?;
let signing_key_pem = parsed
.pkey
.as_ref()
.map(|key| {
key.private_key_to_pem_pkcs8().map_err(|e| {
AsxError::new(
ErrorCode::InvalidInput,
format!("PKCS#12 private key to PEM conversion failed: {e}"),
ErrorContext::new("credentials_from_pkcs12"),
)
})
})
.transpose()?;
Ok(Self {
signing_cert_pem,
signing_key_pem,
recipient_cert_pem: None,
})
}
}
#[cfg(feature = "as2")]
impl From<As2SendCredentials> for PartnerCredentials {
fn from(mut value: As2SendCredentials) -> Self {
let result = Self {
signing_cert_pem: value.signing_cert_pem.take(),
signing_key_pem: value.signing_key_pem.take(),
recipient_cert_pem: value.recipient_cert_pem.take(),
};
std::mem::forget(value);
result
}
}
#[cfg(feature = "as2")]
impl From<PartnerCredentials> for As2SendCredentials {
fn from(value: PartnerCredentials) -> Self {
let mut value = value;
Self {
signing_cert_pem: value.signing_cert_pem.take(),
signing_key_pem: value.signing_key_pem.take(),
recipient_cert_pem: value.recipient_cert_pem.take(),
}
}
}
#[cfg(feature = "as4")]
impl From<As4SendCredentials> for PartnerCredentials {
fn from(mut value: As4SendCredentials) -> Self {
let result = Self {
signing_cert_pem: value.signing_cert_pem.take(),
signing_key_pem: value.signing_key_pem.take(),
recipient_cert_pem: value.recipient_cert_pem.take(),
};
std::mem::forget(value);
result
}
}
#[cfg(feature = "as4")]
impl From<PartnerCredentials> for As4SendCredentials {
fn from(value: PartnerCredentials) -> Self {
let mut value = value;
Self {
signing_cert_pem: value.signing_cert_pem.take(),
signing_key_pem: value.signing_key_pem.take(),
recipient_cert_pem: value.recipient_cert_pem.take(),
}
}
}
#[cfg(test)]
mod tests {
use super::PartnerCredentials;
use std::sync::Arc;
#[cfg(feature = "as2")]
#[test]
fn partner_credentials_roundtrip_as2_projection() {
let src = PartnerCredentials {
signing_cert_pem: Some(Arc::from(b"cert" as &[u8])),
signing_key_pem: Some(b"key".to_vec()),
recipient_cert_pem: Some(Arc::from(b"recipient" as &[u8])),
};
let as2 = src.to_as2_send_credentials();
assert_eq!(as2.signing_cert_pem.as_deref(), Some(b"cert".as_slice()));
assert_eq!(as2.signing_key_pem.as_deref(), Some(b"key".as_slice()));
assert_eq!(
as2.recipient_cert_pem.as_deref(),
Some(b"recipient".as_slice())
);
}
#[cfg(feature = "as4")]
#[test]
fn partner_credentials_roundtrip_as4_projection() {
let src = PartnerCredentials {
signing_cert_pem: Some(Arc::from(b"cert" as &[u8])),
signing_key_pem: Some(b"key".to_vec()),
recipient_cert_pem: Some(Arc::from(b"recipient" as &[u8])),
};
let as4 = src.to_as4_send_credentials();
assert_eq!(as4.signing_cert_pem.as_deref(), Some(b"cert".as_slice()));
assert_eq!(as4.signing_key_pem.as_deref(), Some(b"key".as_slice()));
assert_eq!(
as4.recipient_cert_pem.as_deref(),
Some(b"recipient".as_slice())
);
}
}