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