From 106c5634cf0c4d08987b4c059169f5a4ff82496f Mon Sep 17 00:00:00 2001 From: Facundo Tuesca Date: Fri, 1 Dec 2023 17:53:40 +0100 Subject: [PATCH] Add PKCS7 APIs to access type and signed data certs --- openssl/CHANGELOG.md | 4 ++ openssl/src/pkcs7.rs | 108 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/openssl/CHANGELOG.md b/openssl/CHANGELOG.md index a37da48a9e..95083adaaf 100644 --- a/openssl/CHANGELOG.md +++ b/openssl/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +* Added `Pkcs7Ref::{type_nid,signed_data_certificates}`. + ## [v0.10.61] - 2023-12-04 ### Changed diff --git a/openssl/src/pkcs7.rs b/openssl/src/pkcs7.rs index a272c598b8..ea3bb5cbdb 100644 --- a/openssl/src/pkcs7.rs +++ b/openssl/src/pkcs7.rs @@ -4,16 +4,31 @@ use libc::c_int; use std::mem; use std::ptr; +use crate::asn1::Asn1ObjectRef; use crate::bio::{MemBio, MemBioSlice}; use crate::error::ErrorStack; +use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef}; -use crate::stack::{Stack, StackRef}; +use crate::stack::{Stack, StackRef, Stackable}; use crate::symm::Cipher; +use crate::util::ForeignTypeRefExt; use crate::x509::store::X509StoreRef; use crate::x509::{X509Ref, X509}; use crate::{cvt, cvt_p}; use openssl_macros::corresponds; +foreign_type_and_impl_send_sync! { + type CType = ffi::PKCS7_SIGNER_INFO; + fn drop = ffi::PKCS7_SIGNER_INFO_free; + + pub struct Pkcs7SignerInfo; + pub struct Pkcs7SignerInfoRef; +} + +impl Stackable for Pkcs7SignerInfo { + type StackType = ffi::stack_st_PKCS7_SIGNER_INFO; +} + foreign_type_and_impl_send_sync! { type CType = ffi::PKCS7; fn drop = ffi::PKCS7_free; @@ -281,11 +296,37 @@ impl Pkcs7Ref { Ok(stack) } } + + // Return the type of a PKCS#7 structure as a NID + pub fn type_nid(&self) -> Option<&Asn1ObjectRef> { + unsafe { + let ptr = (*self.as_ptr()).type_; + Asn1ObjectRef::from_const_ptr_opt(ptr) + } + } + + // Retrieve all the certificates from a PKCS#7 structure used for signed data + pub fn signed_data_certificates(&self) -> Result>, ErrorStack> { + unsafe { + if self.type_nid().map(|x| x.nid()) != Some(Nid::PKCS7_SIGNED) { + return Ok(None); + } + // Get the stack of certificates from the PKCS7_SIGNED object + let stack_certs = (*self.as_ptr()) + .d + .sign + .as_mut() + .and_then(|x| x.cert.as_mut()) + .and_then(|x| StackRef::::from_const_ptr_opt(x)); + Ok(stack_certs) + } + } } #[cfg(test)] mod tests { use crate::hash::MessageDigest; + use crate::nid::Nid; use crate::pkcs7::{Pkcs7, Pkcs7Flags}; use crate::pkey::PKey; use crate::stack::Stack; @@ -307,6 +348,10 @@ mod tests { let pkcs7 = Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_ENVELOPED + ); let encrypted = pkcs7 .to_smime(message.as_bytes(), flags) @@ -340,6 +385,10 @@ mod tests { let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_SIGNED + ); let signed = pkcs7 .to_smime(message.as_bytes(), flags) @@ -384,6 +433,10 @@ mod tests { let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_SIGNED + ); let signed = pkcs7 .to_smime(message.as_bytes(), flags) @@ -421,6 +474,10 @@ mod tests { let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_SIGNED + ); let signed = pkcs7 .to_smime(message.as_bytes(), flags) @@ -445,4 +502,53 @@ mod tests { assert!(result.is_err()); } + + #[test] + fn signed_data_certificates() { + let cert = include_bytes!("../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let mut extra_certs = Stack::::new().unwrap(); + for cert in + X509::stack_from_pem(include_bytes!("../test/certs.pem")).expect("should succeed") + { + extra_certs.push(cert).expect("should succeed"); + } + + let message = "foo"; + let flags = Pkcs7Flags::STREAM; + let pkey = include_bytes!("../test/key.pem"); + let pkey = PKey::private_key_from_pem(pkey).unwrap(); + + let pkcs7 = Pkcs7::sign(&cert, &pkey, &extra_certs, message.as_bytes(), flags) + .expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_SIGNED + ); + let signed_data_certs = pkcs7.signed_data_certificates().expect("should succeed"); + assert_eq!(signed_data_certs.expect("should succeed").len(), 3); + } + + #[test] + fn signed_data_certificates_no_signed_data() { + let cert = include_bytes!("../test/certs.pem"); + let cert = X509::from_pem(cert).unwrap(); + let mut certs = Stack::new().unwrap(); + certs.push(cert.clone()).unwrap(); + let message: String = String::from("foo"); + let cipher = Cipher::des_ede3_cbc(); + let flags = Pkcs7Flags::STREAM; + + // Use `Pkcs7::encrypt` since it populates the PKCS7_ENVELOPE struct rather than + // PKCS7_SIGNED + let pkcs7 = + Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed"); + assert_eq!( + pkcs7.type_nid().expect("PKCS7 should have a type").nid(), + Nid::PKCS7_ENVELOPED + ); + + let signed_data_certs = pkcs7.signed_data_certificates().expect("should succeed"); + assert_eq!(signed_data_certs, None); + } }