44
55use std:: borrow:: Cow ;
66use std:: collections:: HashMap ;
7+ use std:: mem;
78use std:: ops:: Deref ;
89use std:: sync:: LazyLock ;
910
11+ use cryptography_x509:: certificate:: Certificate as RawCertificate ;
1012use cryptography_x509:: common:: { AlgorithmIdentifier , AlgorithmParameters } ;
1113use cryptography_x509:: csr:: Attribute ;
1214use cryptography_x509:: pkcs7:: PKCS7_DATA_OID ;
@@ -23,6 +25,7 @@ use crate::buf::CffiBuf;
2325use crate :: error:: { CryptographyError , CryptographyResult } ;
2426use crate :: padding:: PKCS7UnpaddingContext ;
2527use crate :: pkcs12:: symmetric_encrypt;
28+ use crate :: x509:: certificate;
2629#[ cfg( not( any( CRYPTOGRAPHY_IS_BORINGSSL , CRYPTOGRAPHY_IS_AWSLC ) ) ) ]
2730use crate :: x509:: certificate:: load_der_x509_certificate;
2831use crate :: { exceptions, types, x509} ;
@@ -739,65 +742,109 @@ fn load_pkcs7_certificates(
739742 }
740743}
741744
742- #[ pyo3:: pyfunction]
743- fn load_pem_pkcs7_certificates < ' p > (
745+ pub fn try_list_of_certificates < ' p , F > (
744746 py : pyo3:: Python < ' p > ,
745- data : & [ u8 ] ,
746- ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyList > > {
747- cfg_if:: cfg_if! {
748- if #[ cfg( not( any( CRYPTOGRAPHY_IS_BORINGSSL , CRYPTOGRAPHY_IS_AWSLC ) ) ) ] {
749- let pem_block = pem:: parse( data) ?;
750- if pem_block. tag( ) != "PKCS7" {
751- return Err ( CryptographyError :: from(
752- pyo3:: exceptions:: PyValueError :: new_err(
753- "The provided PEM data does not have the PKCS7 tag." ,
754- ) ,
755- ) ) ;
756- }
747+ data : pyo3:: Py < pyo3:: types:: PyBytes > ,
748+ f : F ,
749+ ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyList > >
750+ where
751+ F : for < ' a > FnOnce (
752+ & ' a pyo3:: Py < pyo3:: types:: PyBytes > ,
753+ & mut dyn FnMut ( RawCertificate < ' a > ) -> CryptographyResult < ( ) > ,
754+ ) -> CryptographyResult < ( ) > ,
755+ {
756+ let result = pyo3:: types:: PyList :: empty ( py) ;
757+ let mut cb = |val| {
758+ // SAFETY: based on the type of `F`, we know `val` must be derived from
759+ // data, and we know that `data.clone_ref(py)` makes any pointers into
760+ // the original one also valid.
761+ let raw_cert = certificate:: OwnedCertificate :: new ( data. clone_ref ( py) , |_| unsafe {
762+ mem:: transmute ( val)
763+ } ) ;
764+ result. append ( pyo3:: Bound :: new (
765+ py,
766+ x509:: certificate:: Certificate {
767+ raw : raw_cert,
768+ cached_extensions : pyo3:: sync:: PyOnceLock :: new ( ) ,
769+ } ,
770+ ) ?) ?;
757771
758- load_der_pkcs7_certificates( py, pem_block. contents( ) )
759- } else {
760- let _ = py;
761- let _ = data;
762- Err ( CryptographyError :: from(
772+ Ok ( ( ) )
773+ } ;
774+ f ( & data, & mut cb) ?;
775+
776+ Ok ( result)
777+ }
778+
779+ fn load_pkcs7_certificates_rust (
780+ py : pyo3:: Python < ' _ > ,
781+ data : pyo3:: Py < pyo3:: types:: PyBytes > ,
782+ ) -> CryptographyResult < pyo3:: Bound < ' _ , pyo3:: types:: PyList > > {
783+ try_list_of_certificates ( py, data, |data, cb| {
784+ let p7 = asn1:: parse_single :: < pkcs7:: ContentInfo < ' _ > > ( data. as_bytes ( py) ) ?;
785+ let pkcs7:: Content :: SignedData ( signed_data) = p7. content else {
786+ return Err ( CryptographyError :: from (
763787 exceptions:: UnsupportedAlgorithm :: new_err ( (
764- "PKCS#7 is not supported by this backend ." ,
788+ "Only basic signed structures are currently supported ." ,
765789 exceptions:: Reasons :: UNSUPPORTED_SERIALIZATION ,
766790 ) ) ,
767- ) )
791+ ) ) ;
792+ } ;
793+ let Some ( certs) = signed_data. into_inner ( ) . certificates else {
794+ return Err ( CryptographyError :: from (
795+ pyo3:: exceptions:: PyValueError :: new_err (
796+ "The provided PKCS7 has no certificate data, but a cert loading method was called." ,
797+ ) ,
798+ ) ) ;
799+ } ;
800+ for c in certs. unwrap_read ( ) . clone ( ) {
801+ cb ( c) ?;
768802 }
803+
804+ Ok ( ( ) )
805+ } )
806+ }
807+
808+ #[ pyo3:: pyfunction]
809+ fn load_pem_pkcs7_certificates (
810+ py : pyo3:: Python < ' _ > ,
811+ data : pyo3:: Py < pyo3:: types:: PyBytes > ,
812+ ) -> CryptographyResult < pyo3:: Bound < ' _ , pyo3:: types:: PyList > > {
813+ let pem_block = pem:: parse ( data. as_bytes ( py) ) ?;
814+ if pem_block. tag ( ) != "PKCS7" {
815+ return Err ( CryptographyError :: from (
816+ pyo3:: exceptions:: PyValueError :: new_err (
817+ "The provided PEM data does not have the PKCS7 tag." ,
818+ ) ,
819+ ) ) ;
769820 }
821+ let data = pyo3:: types:: PyBytes :: new ( py, pem_block. contents ( ) ) . unbind ( ) ;
822+
823+ load_der_pkcs7_certificates ( py, data)
770824}
771825
772826#[ pyo3:: pyfunction]
773- fn load_der_pkcs7_certificates < ' p > (
774- py : pyo3:: Python < ' p > ,
775- data : & [ u8 ] ,
776- ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyList > > {
827+ fn load_der_pkcs7_certificates (
828+ py : pyo3:: Python < ' _ > ,
829+ data : pyo3 :: Py < pyo3 :: types :: PyBytes > ,
830+ ) -> CryptographyResult < pyo3:: Bound < ' _ , pyo3:: types:: PyList > > {
777831 cfg_if:: cfg_if! {
778832 if #[ cfg( not( any( CRYPTOGRAPHY_IS_BORINGSSL , CRYPTOGRAPHY_IS_AWSLC ) ) ) ] {
779- let pkcs7_decoded = openssl:: pkcs7:: Pkcs7 :: from_der( data) . map_err( |_| {
833+ let pkcs7_decoded = openssl:: pkcs7:: Pkcs7 :: from_der( data. as_bytes ( py ) ) . map_err( |_| {
780834 CryptographyError :: from( pyo3:: exceptions:: PyValueError :: new_err(
781835 "Unable to parse PKCS7 data" ,
782836 ) )
783837 } ) ?;
784838 let result = load_pkcs7_certificates( py, pkcs7_decoded) ?;
785- if asn1 :: parse_single :: <pkcs7 :: ContentInfo < ' _>> ( data) . is_err( ) {
839+ if load_pkcs7_certificates_rust ( py , data) . is_err( ) {
786840 let warning_cls = pyo3:: exceptions:: PyUserWarning :: type_object( py) ;
787841 let message = c"PKCS#7 certificates could not be parsed as DER, falling back to parsing as BER. Please file an issue at https://github.com/pyca/cryptography/issues explaining how your PKCS#7 certificates were created. In the future, this may become an exception." ;
788842 pyo3:: PyErr :: warn( py, & warning_cls, message, 1 ) ?;
789843 }
790844
791845 Ok ( result)
792846 } else {
793- let _ = py;
794- let _ = data;
795- Err ( CryptographyError :: from(
796- exceptions:: UnsupportedAlgorithm :: new_err( (
797- "PKCS#7 is not supported by this backend." ,
798- exceptions:: Reasons :: UNSUPPORTED_SERIALIZATION ,
799- ) ) ,
800- ) )
847+ load_pkcs7_certificates_rust( py, data)
801848 }
802849 }
803850}
0 commit comments