1 use bitflags::bitflags; 2 use foreign_types::{ForeignType, ForeignTypeRef}; 3 use libc::c_int; 4 use std::mem; 5 use std::ptr; 6 7 use crate::bio::{MemBio, MemBioSlice}; 8 use crate::error::ErrorStack; 9 use crate::pkey::{HasPrivate, PKeyRef}; 10 use crate::stack::{Stack, StackRef}; 11 use crate::symm::Cipher; 12 use crate::x509::store::X509StoreRef; 13 use crate::x509::{X509Ref, X509}; 14 use crate::{cvt, cvt_p}; 15 use openssl_macros::corresponds; 16 17 foreign_type_and_impl_send_sync! { 18 type CType = ffi::PKCS7; 19 fn drop = ffi::PKCS7_free; 20 21 /// A PKCS#7 structure. 22 /// 23 /// Contains signed and/or encrypted data. 24 pub struct Pkcs7; 25 26 /// Reference to `Pkcs7` 27 pub struct Pkcs7Ref; 28 } 29 30 bitflags! { 31 pub struct Pkcs7Flags: c_int { 32 const TEXT = ffi::PKCS7_TEXT; 33 const NOCERTS = ffi::PKCS7_NOCERTS; 34 const NOSIGS = ffi::PKCS7_NOSIGS; 35 const NOCHAIN = ffi::PKCS7_NOCHAIN; 36 const NOINTERN = ffi::PKCS7_NOINTERN; 37 const NOVERIFY = ffi::PKCS7_NOVERIFY; 38 const DETACHED = ffi::PKCS7_DETACHED; 39 const BINARY = ffi::PKCS7_BINARY; 40 const NOATTR = ffi::PKCS7_NOATTR; 41 const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP; 42 const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE; 43 const CRLFEOL = ffi::PKCS7_CRLFEOL; 44 const STREAM = ffi::PKCS7_STREAM; 45 const NOCRL = ffi::PKCS7_NOCRL; 46 const PARTIAL = ffi::PKCS7_PARTIAL; 47 const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST; 48 #[cfg(not(any(ossl101, ossl102, libressl)))] 49 const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT; 50 } 51 } 52 53 impl Pkcs7 { 54 from_pem! { 55 /// Deserializes a PEM-encoded PKCS#7 signature 56 /// 57 /// The input should have a header of `-----BEGIN PKCS7-----`. 58 #[corresponds(PEM_read_bio_PKCS7)] 59 from_pem, 60 Pkcs7, 61 ffi::PEM_read_bio_PKCS7 62 } 63 64 from_der! { 65 /// Deserializes a DER-encoded PKCS#7 signature 66 #[corresponds(d2i_PKCS7)] 67 from_der, 68 Pkcs7, 69 ffi::d2i_PKCS7 70 } 71 72 /// Parses a message in S/MIME format. 73 /// 74 /// Returns the loaded signature, along with the cleartext message (if 75 /// available). 76 #[corresponds(SMIME_read_PKCS7)] from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack>77 pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack> { 78 ffi::init(); 79 80 let input_bio = MemBioSlice::new(input)?; 81 let mut bcont_bio = ptr::null_mut(); 82 unsafe { 83 let pkcs7 = 84 cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?; 85 let out = if !bcont_bio.is_null() { 86 let bcont_bio = MemBio::from_ptr(bcont_bio); 87 Some(bcont_bio.get_buf().to_vec()) 88 } else { 89 None 90 }; 91 Ok((pkcs7, out)) 92 } 93 } 94 95 /// Creates and returns a PKCS#7 `envelopedData` structure. 96 /// 97 /// `certs` is a list of recipient certificates. `input` is the content to be 98 /// encrypted. `cipher` is the symmetric cipher to use. `flags` is an optional 99 /// set of flags. 100 #[corresponds(PKCS7_encrypt)] encrypt( certs: &StackRef<X509>, input: &[u8], cipher: Cipher, flags: Pkcs7Flags, ) -> Result<Pkcs7, ErrorStack>101 pub fn encrypt( 102 certs: &StackRef<X509>, 103 input: &[u8], 104 cipher: Cipher, 105 flags: Pkcs7Flags, 106 ) -> Result<Pkcs7, ErrorStack> { 107 let input_bio = MemBioSlice::new(input)?; 108 109 unsafe { 110 cvt_p(ffi::PKCS7_encrypt( 111 certs.as_ptr(), 112 input_bio.as_ptr(), 113 cipher.as_ptr(), 114 flags.bits, 115 )) 116 .map(Pkcs7) 117 } 118 } 119 120 /// Creates and returns a PKCS#7 `signedData` structure. 121 /// 122 /// `signcert` is the certificate to sign with, `pkey` is the corresponding 123 /// private key. `certs` is an optional additional set of certificates to 124 /// include in the PKCS#7 structure (for example any intermediate CAs in the 125 /// chain). 126 #[corresponds(PKCS7_sign)] sign<PT>( signcert: &X509Ref, pkey: &PKeyRef<PT>, certs: &StackRef<X509>, input: &[u8], flags: Pkcs7Flags, ) -> Result<Pkcs7, ErrorStack> where PT: HasPrivate,127 pub fn sign<PT>( 128 signcert: &X509Ref, 129 pkey: &PKeyRef<PT>, 130 certs: &StackRef<X509>, 131 input: &[u8], 132 flags: Pkcs7Flags, 133 ) -> Result<Pkcs7, ErrorStack> 134 where 135 PT: HasPrivate, 136 { 137 let input_bio = MemBioSlice::new(input)?; 138 unsafe { 139 cvt_p(ffi::PKCS7_sign( 140 signcert.as_ptr(), 141 pkey.as_ptr(), 142 certs.as_ptr(), 143 input_bio.as_ptr(), 144 flags.bits, 145 )) 146 .map(Pkcs7) 147 } 148 } 149 } 150 151 impl Pkcs7Ref { 152 /// Converts PKCS#7 structure to S/MIME format 153 #[corresponds(SMIME_write_PKCS7)] to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack>154 pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack> { 155 let input_bio = MemBioSlice::new(input)?; 156 let output = MemBio::new()?; 157 unsafe { 158 cvt(ffi::SMIME_write_PKCS7( 159 output.as_ptr(), 160 self.as_ptr(), 161 input_bio.as_ptr(), 162 flags.bits, 163 )) 164 .map(|_| output.get_buf().to_owned()) 165 } 166 } 167 168 to_pem! { 169 /// Serializes the data into a PEM-encoded PKCS#7 structure. 170 /// 171 /// The output will have a header of `-----BEGIN PKCS7-----`. 172 #[corresponds(PEM_write_bio_PKCS7)] 173 to_pem, 174 ffi::PEM_write_bio_PKCS7 175 } 176 177 to_der! { 178 /// Serializes the data into a DER-encoded PKCS#7 structure. 179 #[corresponds(i2d_PKCS7)] 180 to_der, 181 ffi::i2d_PKCS7 182 } 183 184 /// Decrypts data using the provided private key. 185 /// 186 /// `pkey` is the recipient's private key, and `cert` is the recipient's 187 /// certificate. 188 /// 189 /// Returns the decrypted message. 190 #[corresponds(PKCS7_decrypt)] decrypt<PT>( &self, pkey: &PKeyRef<PT>, cert: &X509Ref, flags: Pkcs7Flags, ) -> Result<Vec<u8>, ErrorStack> where PT: HasPrivate,191 pub fn decrypt<PT>( 192 &self, 193 pkey: &PKeyRef<PT>, 194 cert: &X509Ref, 195 flags: Pkcs7Flags, 196 ) -> Result<Vec<u8>, ErrorStack> 197 where 198 PT: HasPrivate, 199 { 200 let output = MemBio::new()?; 201 202 unsafe { 203 cvt(ffi::PKCS7_decrypt( 204 self.as_ptr(), 205 pkey.as_ptr(), 206 cert.as_ptr(), 207 output.as_ptr(), 208 flags.bits, 209 )) 210 .map(|_| output.get_buf().to_owned()) 211 } 212 } 213 214 /// Verifies the PKCS#7 `signedData` structure contained by `&self`. 215 /// 216 /// `certs` is a set of certificates in which to search for the signer's 217 /// certificate. `store` is a trusted certificate store (used for chain 218 /// verification). `indata` is the signed data if the content is not present 219 /// in `&self`. The content is written to `out` if it is not `None`. 220 #[corresponds(PKCS7_verify)] verify( &self, certs: &StackRef<X509>, store: &X509StoreRef, indata: Option<&[u8]>, out: Option<&mut Vec<u8>>, flags: Pkcs7Flags, ) -> Result<(), ErrorStack>221 pub fn verify( 222 &self, 223 certs: &StackRef<X509>, 224 store: &X509StoreRef, 225 indata: Option<&[u8]>, 226 out: Option<&mut Vec<u8>>, 227 flags: Pkcs7Flags, 228 ) -> Result<(), ErrorStack> { 229 let out_bio = MemBio::new()?; 230 231 let indata_bio = match indata { 232 Some(data) => Some(MemBioSlice::new(data)?), 233 None => None, 234 }; 235 let indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr()); 236 237 unsafe { 238 cvt(ffi::PKCS7_verify( 239 self.as_ptr(), 240 certs.as_ptr(), 241 store.as_ptr(), 242 indata_bio_ptr, 243 out_bio.as_ptr(), 244 flags.bits, 245 )) 246 .map(|_| ())? 247 } 248 249 if let Some(data) = out { 250 data.clear(); 251 data.extend_from_slice(out_bio.get_buf()); 252 } 253 254 Ok(()) 255 } 256 257 /// Retrieve the signer's certificates from the PKCS#7 structure without verifying them. 258 #[corresponds(PKCS7_get0_signers)] signers( &self, certs: &StackRef<X509>, flags: Pkcs7Flags, ) -> Result<Stack<X509>, ErrorStack>259 pub fn signers( 260 &self, 261 certs: &StackRef<X509>, 262 flags: Pkcs7Flags, 263 ) -> Result<Stack<X509>, ErrorStack> { 264 unsafe { 265 let ptr = cvt_p(ffi::PKCS7_get0_signers( 266 self.as_ptr(), 267 certs.as_ptr(), 268 flags.bits, 269 ))?; 270 271 // The returned stack is owned by the caller, but the certs inside are not! Our stack interface can't deal 272 // with that, so instead we just manually bump the refcount of the certs so that the whole stack is properly 273 // owned. 274 let stack = Stack::<X509>::from_ptr(ptr); 275 for cert in &stack { 276 mem::forget(cert.to_owned()); 277 } 278 279 Ok(stack) 280 } 281 } 282 } 283 284 #[cfg(test)] 285 mod tests { 286 use crate::hash::MessageDigest; 287 use crate::pkcs7::{Pkcs7, Pkcs7Flags}; 288 use crate::pkey::PKey; 289 use crate::stack::Stack; 290 use crate::symm::Cipher; 291 use crate::x509::store::X509StoreBuilder; 292 use crate::x509::X509; 293 294 #[test] encrypt_decrypt_test()295 fn encrypt_decrypt_test() { 296 let cert = include_bytes!("../test/certs.pem"); 297 let cert = X509::from_pem(cert).unwrap(); 298 let mut certs = Stack::new().unwrap(); 299 certs.push(cert.clone()).unwrap(); 300 let message: String = String::from("foo"); 301 let cipher = Cipher::des_ede3_cbc(); 302 let flags = Pkcs7Flags::STREAM; 303 let pkey = include_bytes!("../test/key.pem"); 304 let pkey = PKey::private_key_from_pem(pkey).unwrap(); 305 306 let pkcs7 = 307 Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed"); 308 309 let encrypted = pkcs7 310 .to_smime(message.as_bytes(), flags) 311 .expect("should succeed"); 312 313 let (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed"); 314 315 let decoded = pkcs7_decoded 316 .decrypt(&pkey, &cert, Pkcs7Flags::empty()) 317 .expect("should succeed"); 318 319 assert_eq!(decoded, message.into_bytes()); 320 } 321 322 #[test] sign_verify_test_detached()323 fn sign_verify_test_detached() { 324 let cert = include_bytes!("../test/cert.pem"); 325 let cert = X509::from_pem(cert).unwrap(); 326 let certs = Stack::new().unwrap(); 327 let message = "foo"; 328 let flags = Pkcs7Flags::STREAM | Pkcs7Flags::DETACHED; 329 let pkey = include_bytes!("../test/key.pem"); 330 let pkey = PKey::private_key_from_pem(pkey).unwrap(); 331 let mut store_builder = X509StoreBuilder::new().expect("should succeed"); 332 333 let root_ca = include_bytes!("../test/root-ca.pem"); 334 let root_ca = X509::from_pem(root_ca).unwrap(); 335 store_builder.add_cert(root_ca).expect("should succeed"); 336 337 let store = store_builder.build(); 338 339 let pkcs7 = 340 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); 341 342 let signed = pkcs7 343 .to_smime(message.as_bytes(), flags) 344 .expect("should succeed"); 345 println!("{:?}", String::from_utf8(signed.clone()).unwrap()); 346 let (pkcs7_decoded, content) = 347 Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); 348 349 let mut output = Vec::new(); 350 pkcs7_decoded 351 .verify( 352 &certs, 353 &store, 354 Some(message.as_bytes()), 355 Some(&mut output), 356 flags, 357 ) 358 .expect("should succeed"); 359 360 assert_eq!(output, message.as_bytes()); 361 assert_eq!(content.expect("should be non-empty"), message.as_bytes()); 362 } 363 364 /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2 365 #[test] 366 #[cfg_attr(all(libressl360, not(libressl361)), ignore)] sign_verify_test_normal()367 fn sign_verify_test_normal() { 368 let cert = include_bytes!("../test/cert.pem"); 369 let cert = X509::from_pem(cert).unwrap(); 370 let certs = Stack::new().unwrap(); 371 let message = "foo"; 372 let flags = Pkcs7Flags::STREAM; 373 let pkey = include_bytes!("../test/key.pem"); 374 let pkey = PKey::private_key_from_pem(pkey).unwrap(); 375 let mut store_builder = X509StoreBuilder::new().expect("should succeed"); 376 377 let root_ca = include_bytes!("../test/root-ca.pem"); 378 let root_ca = X509::from_pem(root_ca).unwrap(); 379 store_builder.add_cert(root_ca).expect("should succeed"); 380 381 let store = store_builder.build(); 382 383 let pkcs7 = 384 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); 385 386 let signed = pkcs7 387 .to_smime(message.as_bytes(), flags) 388 .expect("should succeed"); 389 390 let (pkcs7_decoded, content) = 391 Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); 392 393 let mut output = Vec::new(); 394 pkcs7_decoded 395 .verify(&certs, &store, None, Some(&mut output), flags) 396 .expect("should succeed"); 397 398 assert_eq!(output, message.as_bytes()); 399 assert!(content.is_none()); 400 } 401 402 /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2 403 #[test] 404 #[cfg_attr(all(libressl360, not(libressl361)), ignore)] signers()405 fn signers() { 406 let cert = include_bytes!("../test/cert.pem"); 407 let cert = X509::from_pem(cert).unwrap(); 408 let cert_digest = cert.digest(MessageDigest::sha256()).unwrap(); 409 let certs = Stack::new().unwrap(); 410 let message = "foo"; 411 let flags = Pkcs7Flags::STREAM; 412 let pkey = include_bytes!("../test/key.pem"); 413 let pkey = PKey::private_key_from_pem(pkey).unwrap(); 414 let mut store_builder = X509StoreBuilder::new().expect("should succeed"); 415 416 let root_ca = include_bytes!("../test/root-ca.pem"); 417 let root_ca = X509::from_pem(root_ca).unwrap(); 418 store_builder.add_cert(root_ca).expect("should succeed"); 419 420 let pkcs7 = 421 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); 422 423 let signed = pkcs7 424 .to_smime(message.as_bytes(), flags) 425 .expect("should succeed"); 426 427 let (pkcs7_decoded, _) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); 428 429 let empty_certs = Stack::new().unwrap(); 430 let signer_certs = pkcs7_decoded 431 .signers(&empty_certs, flags) 432 .expect("should succeed"); 433 assert_eq!(empty_certs.len(), 0); 434 assert_eq!(signer_certs.len(), 1); 435 let signer_digest = signer_certs[0].digest(MessageDigest::sha256()).unwrap(); 436 assert_eq!(*cert_digest, *signer_digest); 437 } 438 439 #[test] invalid_from_smime()440 fn invalid_from_smime() { 441 let input = String::from("Invalid SMIME Message"); 442 let result = Pkcs7::from_smime(input.as_bytes()); 443 444 assert!(result.is_err()); 445 } 446 } 447