@@ -670,13 +670,13 @@ fn compute_pkcs7_signature_algorithm<'p>(
670
670
}
671
671
672
672
#[ pyo3:: pyfunction]
673
- #[ pyo3( signature = ( signature, content, certificate, options) ) ]
673
+ #[ pyo3( signature = ( signature, content = None , certificate = None , options = None ) ) ]
674
674
fn verify_smime < ' p > (
675
675
py : pyo3:: Python < ' p > ,
676
676
signature : & [ u8 ] ,
677
677
content : Option < & [ u8 ] > ,
678
- certificate : pyo3:: Bound < ' p , x509:: certificate:: Certificate > ,
679
- options : & pyo3:: Bound < ' p , pyo3:: types:: PyList > ,
678
+ certificate : Option < pyo3:: Bound < ' p , x509:: certificate:: Certificate > > ,
679
+ options : Option < & pyo3:: Bound < ' p , pyo3:: types:: PyList > > ,
680
680
) -> CryptographyResult < ( ) > {
681
681
// Parse the email
682
682
let py_content_and_signature = types:: SMIME_SIGNED_DECODE . get ( py) ?. call1 ( ( signature, ) ) ?;
@@ -704,13 +704,13 @@ fn verify_smime<'p>(
704
704
}
705
705
706
706
#[ pyo3:: pyfunction]
707
- #[ pyo3( signature = ( signature, content, certificate, options) ) ]
707
+ #[ pyo3( signature = ( signature, content = None , certificate = None , options = None ) ) ]
708
708
fn verify_pem < ' p > (
709
709
py : pyo3:: Python < ' p > ,
710
710
signature : & [ u8 ] ,
711
711
content : Option < & [ u8 ] > ,
712
- certificate : pyo3:: Bound < ' p , x509:: certificate:: Certificate > ,
713
- options : & pyo3:: Bound < ' p , pyo3:: types:: PyList > ,
712
+ certificate : Option < pyo3:: Bound < ' p , x509:: certificate:: Certificate > > ,
713
+ options : Option < & pyo3:: Bound < ' p , pyo3:: types:: PyList > > ,
714
714
) -> CryptographyResult < ( ) > {
715
715
let pem_str = std:: str:: from_utf8 ( signature)
716
716
. map_err ( |_| pyo3:: exceptions:: PyValueError :: new_err ( "Invalid PEM data" ) ) ?;
@@ -730,15 +730,19 @@ fn verify_pem<'p>(
730
730
}
731
731
732
732
#[ pyo3:: pyfunction]
733
- #[ pyo3( signature = ( signature, content, certificate, options) ) ]
733
+ #[ pyo3( signature = ( signature, content = None , certificate = None , options = None ) ) ]
734
734
fn verify_der < ' p > (
735
735
py : pyo3:: Python < ' p > ,
736
736
signature : & [ u8 ] ,
737
737
content : Option < & [ u8 ] > ,
738
- certificate : pyo3:: Bound < ' p , x509:: certificate:: Certificate > ,
739
- options : & pyo3:: Bound < ' p , pyo3:: types:: PyList > ,
738
+ certificate : Option < pyo3:: Bound < ' p , x509:: certificate:: Certificate > > ,
739
+ options : Option < & pyo3:: Bound < ' p , pyo3:: types:: PyList > > ,
740
740
) -> CryptographyResult < ( ) > {
741
741
// Check the verify options
742
+ let options = match options {
743
+ Some ( options) => options,
744
+ None => & pyo3:: types:: PyList :: empty ( py) ,
745
+ } ;
742
746
check_verify_options ( py, options) ?;
743
747
744
748
// Verify the data
@@ -748,57 +752,116 @@ fn verify_der<'p>(
748
752
// Extract signed data
749
753
let signed_data = signed_data. into_inner ( ) ;
750
754
751
- // Get recipients, and find the one matching with the signer infos (if any)
752
- // TODO: what to do for multiple certificates?
753
- let mut signer_infos = signed_data. signer_infos . unwrap_read ( ) . clone ( ) ;
754
- let signer_certificate = certificate. get ( ) . raw . borrow_dependent ( ) ;
755
- let signer_serial_number = signer_certificate. tbs_cert . serial ;
756
- let signer_issuer = signer_certificate. tbs_cert . issuer . clone ( ) ;
757
- let found_signer_info = signer_infos. find ( |info| {
758
- info. issuer_and_serial_number . serial_number == signer_serial_number
759
- && info. issuer_and_serial_number . issuer == signer_issuer
760
- } ) ;
761
-
762
- // Raise error when no signer is found
763
- let signer_info = match found_signer_info {
764
- Some ( info) => info,
755
+ // Extract the signer certificate: either from given value, or from signed data
756
+ let certificate = match certificate {
757
+ Some ( cert) => cert,
765
758
None => {
766
- return Err ( CryptographyError :: from (
767
- pyo3:: exceptions:: PyValueError :: new_err (
768
- "No signer found that matches the given certificate." ,
769
- ) ,
770
- ) ) ;
759
+ let certificates = signed_data. certificates ;
760
+ match certificates {
761
+ Some ( certificates) => {
762
+ let mut certificates = certificates. unwrap_read ( ) . clone ( ) ;
763
+ match certificates. next ( ) {
764
+ Some ( cert) => {
765
+ let cert_bytes =
766
+ pyo3:: types:: PyBytes :: new ( py, & asn1:: write_single ( & cert) ?)
767
+ . unbind ( ) ;
768
+ let py_cert = load_der_x509_certificate ( py, cert_bytes, None ) ?;
769
+ pyo3:: Bound :: new ( py, py_cert) ?
770
+ }
771
+ None => {
772
+ return Err ( CryptographyError :: from (
773
+ pyo3:: exceptions:: PyValueError :: new_err (
774
+ "The PKCS7 data does not contain any certificate." ,
775
+ ) ,
776
+ ) ) ;
777
+ }
778
+ }
779
+ }
780
+ None => {
781
+ return Err ( CryptographyError :: from (
782
+ pyo3:: exceptions:: PyValueError :: new_err (
783
+ "The PKCS7 data does not contain any certificate." ,
784
+ ) ,
785
+ ) ) ;
786
+ }
787
+ }
771
788
}
772
789
} ;
773
790
774
- // Prepare the content: try to use the authenticated attributes, then the content stored
775
- // in the signed data, then the provided content. If None of these are available, raise
776
- // an error. TODO: what should the order be?
777
- let data = match signer_info. authenticated_attributes {
778
- Some ( attrs) => Cow :: Owned ( asn1:: write_single ( & attrs) ?) ,
779
- None => match content {
780
- Some ( data) => Cow :: Borrowed ( data) ,
781
- None => match signed_data. content_info . content {
782
- pkcs7:: Content :: Data ( Some ( data) ) => Cow :: Borrowed ( data. into_inner ( ) ) ,
791
+ // Get recipients, and find the one matching with the signer infos (if any)
792
+ // TODO: what to do for multiple certificates?
793
+ if !options. contains ( types:: PKCS7_NO_SIGS . get ( py) ?) ? {
794
+ let mut signer_infos = signed_data. signer_infos . unwrap_read ( ) . clone ( ) ;
795
+ let signer_certificate = certificate. get ( ) . raw . borrow_dependent ( ) ;
796
+ let signer_serial_number = signer_certificate. tbs_cert . serial ;
797
+ let signer_issuer = signer_certificate. tbs_cert . issuer . clone ( ) ;
798
+ let found_signer_info = signer_infos. find ( |info| {
799
+ info. issuer_and_serial_number . serial_number == signer_serial_number
800
+ && info. issuer_and_serial_number . issuer == signer_issuer
801
+ } ) ;
802
+
803
+ // Raise error when no signer is found
804
+ let signer_info = match found_signer_info {
805
+ Some ( info) => info,
806
+ None => {
807
+ return Err ( CryptographyError :: from (
808
+ pyo3:: exceptions:: PyValueError :: new_err (
809
+ "No signer found that matches the given certificate." ,
810
+ ) ,
811
+ ) ) ;
812
+ }
813
+ } ;
814
+
815
+ // Prepare the content: try to use the authenticated attributes, then the content stored
816
+ // in the signed data, then the provided content. If None of these are available, raise
817
+ // an error. TODO: what should the order be?
818
+ let data = match signer_info. authenticated_attributes {
819
+ Some ( attrs) => Cow :: Owned ( asn1:: write_single ( & attrs) ?) ,
820
+ None => match content {
821
+ Some ( data) => Cow :: Borrowed ( data) ,
822
+ None => match signed_data. content_info . content {
823
+ pkcs7:: Content :: Data ( Some ( data) ) => Cow :: Borrowed ( data. into_inner ( ) ) ,
824
+ _ => {
825
+ return Err ( CryptographyError :: from (
826
+ pyo3:: exceptions:: PyValueError :: new_err (
827
+ "No content stored in the signature or provided." ,
828
+ ) ,
829
+ ) ) ;
830
+ }
831
+ } ,
832
+ } ,
833
+ } ;
834
+
835
+ // For RSA signatures (with no PSS padding), the OID is always the same no matter the
836
+ // digest algorithm. We need to modify the algorithm identifier to add the hash
837
+ // algorithm information. We are checking for RSA-256, which the S/MIME v3.2 RFC
838
+ // specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.2)
839
+ let signature_algorithm = match signer_info. digest_encryption_algorithm . oid ( ) {
840
+ & oid:: RSA_OID => match signer_info. digest_algorithm . oid ( ) {
841
+ & oid:: SHA256_OID => common:: AlgorithmIdentifier {
842
+ oid : asn1:: DefinedByMarker :: marker ( ) ,
843
+ params : common:: AlgorithmParameters :: RsaWithSha256 ( Some ( ( ) ) ) ,
844
+ } ,
783
845
_ => {
784
846
return Err ( CryptographyError :: from (
785
847
pyo3:: exceptions:: PyValueError :: new_err (
786
- "No content stored in the signature or provided ." ,
848
+ "Unsupported hash algorithm with RSA ." ,
787
849
) ,
788
- ) ) ;
850
+ ) )
789
851
}
790
852
} ,
791
- } ,
792
- } ;
793
-
794
- // Verify the signature
795
- x509:: sign:: verify_signature_with_signature_algorithm (
796
- py,
797
- certificate. call_method0 ( pyo3:: intern!( py, "public_key" ) ) ?,
798
- & signer_info. digest_encryption_algorithm ,
799
- signer_info. encrypted_digest ,
800
- & data,
801
- ) ?;
853
+ _ => signer_info. digest_encryption_algorithm ,
854
+ } ;
855
+
856
+ // Verify the signature
857
+ x509:: sign:: verify_signature_with_signature_algorithm (
858
+ py,
859
+ certificate. call_method0 ( pyo3:: intern!( py, "public_key" ) ) ?,
860
+ & signature_algorithm,
861
+ signer_info. encrypted_digest ,
862
+ & data,
863
+ ) ?;
864
+ }
802
865
803
866
// Verify the certificate
804
867
if !options. contains ( types:: PKCS7_NO_VERIFY . get ( py) ?) ? {
@@ -839,11 +902,12 @@ fn check_verify_options<'p>(
839
902
840
903
// Check if any option is not PKCS7Options::NoVerify
841
904
let no_verify_option = types:: PKCS7_NO_VERIFY . get ( py) ?;
905
+ let no_sigs_option = types:: PKCS7_NO_SIGS . get ( py) ?;
842
906
for opt in options. iter ( ) {
843
- if ! opt. eq ( no_verify_option. clone ( ) ) ? {
907
+ if opt. ne ( no_verify_option. clone ( ) ) ? & opt . ne ( no_sigs_option . clone ( ) ) ? {
844
908
return Err ( CryptographyError :: from (
845
909
pyo3:: exceptions:: PyValueError :: new_err (
846
- "Only the following options are supported for verification: NoVerify" ,
910
+ "Only the following options are supported for verification: NoVerify, NoSigs " ,
847
911
) ,
848
912
) ) ;
849
913
}
0 commit comments