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