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