1use crate::core::{ErrorCode, Result};
8use std::sync::Arc;
9use zeroize::Zeroize;
10
11#[cfg(feature = "as2")]
12use crate::as2::{As2PreparedSendCredentials, As2SendCredentials, As2SendPolicy};
13#[cfg(feature = "as4")]
14use crate::as4::{As4PreparedSendCredentials, As4SendCredentials, As4SendPolicy};
15
16#[derive(Clone, Default)]
21pub struct PartnerCredentials {
22 pub signing_cert_pem: Option<Arc<[u8]>>,
24 pub signing_key_pem: Option<Vec<u8>>,
26 pub recipient_cert_pem: Option<Arc<[u8]>>,
28}
29
30impl std::fmt::Debug for PartnerCredentials {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("PartnerCredentials")
34 .field("signing_cert_pem", &self.signing_cert_pem)
35 .field(
36 "signing_key_pem",
37 &crate::core::redact_present(self.signing_key_pem.is_some()),
38 )
39 .field("recipient_cert_pem", &self.recipient_cert_pem)
40 .finish()
41 }
42}
43
44impl Drop for PartnerCredentials {
45 fn drop(&mut self) {
46 if let Some(key) = self.signing_key_pem.as_mut() {
47 key.zeroize();
48 }
49 }
50}
51
52impl PartnerCredentials {
53 #[cfg(feature = "as2")]
55 pub fn to_as2_send_credentials(&self) -> As2SendCredentials {
56 As2SendCredentials {
57 signing_cert_pem: self.signing_cert_pem.clone(),
58 signing_key_pem: self.signing_key_pem.clone(),
59 recipient_cert_pem: self.recipient_cert_pem.clone(),
60 }
61 }
62
63 #[cfg(feature = "as4")]
65 pub fn to_as4_send_credentials(&self) -> As4SendCredentials {
66 As4SendCredentials {
67 signing_cert_pem: self.signing_cert_pem.clone(),
68 signing_key_pem: self.signing_key_pem.clone(),
69 recipient_cert_pem: self.recipient_cert_pem.clone(),
70 }
71 }
72
73 #[cfg(feature = "as2")]
75 pub fn prepare_as2_for_policy(
76 &self,
77 policy: &As2SendPolicy,
78 stage: &'static str,
79 error_code: ErrorCode,
80 ) -> Result<As2PreparedSendCredentials> {
81 self.to_as2_send_credentials()
82 .prepare_for_policy(policy, stage, error_code)
83 }
84
85 #[cfg(feature = "as4")]
87 pub fn prepare_as4_for_policy(
88 &self,
89 policy: &As4SendPolicy,
90 stage: &'static str,
91 error_code: ErrorCode,
92 ) -> Result<As4PreparedSendCredentials> {
93 self.to_as4_send_credentials()
94 .prepare_for_policy(policy, stage, error_code)
95 }
96
97 #[cfg(any(feature = "as2", feature = "as4"))]
121 pub fn from_pkcs12(der: &[u8], passphrase: &str) -> crate::core::Result<Self> {
122 use crate::core::{AsxError, ErrorContext};
123 use openssl::pkcs12::Pkcs12;
124
125 let pkcs12 = Pkcs12::from_der(der).map_err(|e| {
126 AsxError::new(
127 ErrorCode::InvalidInput,
128 format!("PKCS#12 DER parsing failed: {e}"),
129 ErrorContext::new("credentials_from_pkcs12"),
130 )
131 })?;
132
133 let parsed = pkcs12.parse2(passphrase).map_err(|e| {
134 AsxError::new(
135 ErrorCode::InvalidInput,
136 format!("PKCS#12 passphrase or structure error: {e}"),
137 ErrorContext::new("credentials_from_pkcs12"),
138 )
139 })?;
140
141 let signing_cert_pem = parsed
142 .cert
143 .as_ref()
144 .map(|cert| {
145 cert.to_pem()
146 .map_err(|e| {
147 AsxError::new(
148 ErrorCode::InvalidInput,
149 format!("PKCS#12 certificate to PEM conversion failed: {e}"),
150 ErrorContext::new("credentials_from_pkcs12"),
151 )
152 })
153 .map(Arc::from)
154 })
155 .transpose()?;
156
157 let signing_key_pem = parsed
158 .pkey
159 .as_ref()
160 .map(|key| {
161 key.private_key_to_pem_pkcs8().map_err(|e| {
162 AsxError::new(
163 ErrorCode::InvalidInput,
164 format!("PKCS#12 private key to PEM conversion failed: {e}"),
165 ErrorContext::new("credentials_from_pkcs12"),
166 )
167 })
168 })
169 .transpose()?;
170
171 Ok(Self {
172 signing_cert_pem,
173 signing_key_pem,
174 recipient_cert_pem: None,
175 })
176 }
177}
178
179#[cfg(feature = "as2")]
180impl From<As2SendCredentials> for PartnerCredentials {
181 fn from(mut value: As2SendCredentials) -> Self {
182 let result = Self {
183 signing_cert_pem: value.signing_cert_pem.take(),
184 signing_key_pem: value.signing_key_pem.take(),
185 recipient_cert_pem: value.recipient_cert_pem.take(),
186 };
187 std::mem::forget(value);
192 result
193 }
194}
195
196#[cfg(feature = "as2")]
197impl From<PartnerCredentials> for As2SendCredentials {
198 fn from(value: PartnerCredentials) -> Self {
199 let mut value = value;
200 Self {
201 signing_cert_pem: value.signing_cert_pem.take(),
202 signing_key_pem: value.signing_key_pem.take(),
203 recipient_cert_pem: value.recipient_cert_pem.take(),
204 }
205 }
206}
207
208#[cfg(feature = "as4")]
209impl From<As4SendCredentials> for PartnerCredentials {
210 fn from(mut value: As4SendCredentials) -> Self {
211 let result = Self {
212 signing_cert_pem: value.signing_cert_pem.take(),
213 signing_key_pem: value.signing_key_pem.take(),
214 recipient_cert_pem: value.recipient_cert_pem.take(),
215 };
216 std::mem::forget(value);
219 result
220 }
221}
222
223#[cfg(feature = "as4")]
224impl From<PartnerCredentials> for As4SendCredentials {
225 fn from(value: PartnerCredentials) -> Self {
226 let mut value = value;
227 Self {
228 signing_cert_pem: value.signing_cert_pem.take(),
229 signing_key_pem: value.signing_key_pem.take(),
230 recipient_cert_pem: value.recipient_cert_pem.take(),
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::PartnerCredentials;
238 use std::sync::Arc;
239
240 #[cfg(feature = "as2")]
241 #[test]
242 fn partner_credentials_roundtrip_as2_projection() {
243 let src = PartnerCredentials {
244 signing_cert_pem: Some(Arc::from(b"cert" as &[u8])),
245 signing_key_pem: Some(b"key".to_vec()),
246 recipient_cert_pem: Some(Arc::from(b"recipient" as &[u8])),
247 };
248
249 let as2 = src.to_as2_send_credentials();
250 assert_eq!(as2.signing_cert_pem.as_deref(), Some(b"cert".as_slice()));
251 assert_eq!(as2.signing_key_pem.as_deref(), Some(b"key".as_slice()));
252 assert_eq!(
253 as2.recipient_cert_pem.as_deref(),
254 Some(b"recipient".as_slice())
255 );
256 }
257
258 #[cfg(feature = "as4")]
259 #[test]
260 fn partner_credentials_roundtrip_as4_projection() {
261 let src = PartnerCredentials {
262 signing_cert_pem: Some(Arc::from(b"cert" as &[u8])),
263 signing_key_pem: Some(b"key".to_vec()),
264 recipient_cert_pem: Some(Arc::from(b"recipient" as &[u8])),
265 };
266
267 let as4 = src.to_as4_send_credentials();
268 assert_eq!(as4.signing_cert_pem.as_deref(), Some(b"cert".as_slice()));
269 assert_eq!(as4.signing_key_pem.as_deref(), Some(b"key".as_slice()));
270 assert_eq!(
271 as4.recipient_cert_pem.as_deref(),
272 Some(b"recipient".as_slice())
273 );
274 }
275}