diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4e1118db8dc3..3658ac5855d7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,12 @@ Changelog * Removed the deprecated ``CAST5``, ``SEED``, ``IDEA``, and ``Blowfish`` classes from the cipher module. These are still available in :doc:`/hazmat/decrepit/index`. +* Added support for PKCS7 decryption & encryption using AES-256 as content algorithm, + in addition to AES-128. +* Added basic support for PKCS7 verification (including S/MIME 3.2) via + :func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_verify_der`, + :func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_verify_pem`, and + :func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_verify_smime`. .. _v45-0-4: diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 54b645e2b507..d01c6dcbb578 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -1003,6 +1003,15 @@ Custom PKCS7 Test Vectors * ``pkcs7/enveloped-no-content.der``- A DER encoded PKCS7 file with enveloped data, without encrypted content, with key encrypted under the public key of ``x509/custom/ca/rsa_ca.pem``. +* ``pkcs7/ca.pem`` - A certificate adapted for S/MIME signature & verification. + Its private key is ``pkcs7/ca_key.pem`` . +* ``pkcs7/ca_ascii_san.pem`` - An invalid certificate adapted for S/MIME signature + & verification. It has an ASCII subject alternative name stored as `otherName`. +* ``pkcs7/ca_non_ascii_san.pem`` - An invalid certificate adapted for S/MIME signature + & verification. It has an non-ASCII subject alternative name stored as `rfc822Name`. +* ``pkcs7/signed-opaque.msg``- A PKCS7 signed message, signed using opaque + signing (``application/pkcs7-mime`` content type), signed under the + private key of ``x509/custom/ca/ca.pem``, ``x509/custom/ca/ca_key.pem``. Custom OpenSSH Test Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index fb49c7d14fb7..1c879069124f 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -1262,6 +1262,28 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, -----END PRIVATE KEY----- """.strip() + verify_cert = b""" + -----BEGIN CERTIFICATE----- + MIIBhjCCASygAwIBAgICAwkwCgYIKoZIzj0EAwIwJzELMAkGA1UEBhMCVVMxGDAW + BgNVBAMMD2NyeXB0b2dyYXBoeSBDQTAgFw0xNzAxMDEwMTAwMDBaGA8yMTAwMDEw + MTAwMDAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBD + QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N + 2CxSJE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjRjBE + MCEGA1UdEQEB/wQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wHwYDVR0jBBgwFoAU + /Ou02BLyyT2Zwzxn9H03feYT7fowCgYIKoZIzj0EAwIDSAAwRQIgUwIdC0Emkd6f + 17DeOXTlmTAhwSDJ2FTuyHESwei7wJcCIQCnr9NpBxbtJfEzxHGGyd7PxgpOLi5u + rk+8QfzGMmg/fw== + -----END CERTIFICATE----- + """.strip() + + verify_key = b""" + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe + jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs + UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF + -----END PRIVATE KEY----- + """.strip() + .. class:: PKCS7SignatureBuilder The PKCS7 signature builder can create both basic PKCS7 signed messages as @@ -1340,6 +1362,150 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, :returns bytes: The signed PKCS7 message. +.. function:: pkcs7_verify_der(data, content=None, certificate=None, options=None) + + .. versionadded:: 45.0.0 + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes, serialization + >>> from cryptography.hazmat.primitives.serialization import pkcs7 + >>> cert = x509.load_pem_x509_certificate(verify_cert) + >>> key = serialization.load_pem_private_key(verify_key, None) + >>> signed = pkcs7.PKCS7SignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.DER, [] + ... ) + >>> pkcs7.pkcs7_verify_der(signed) + + Deserialize and verify a DER-encoded PKCS7 signed message. PKCS7 (or S/MIME) has multiple + versions, but this supports a subset of :rfc:`5751`, also known as S/MIME Version 3.2. If the + verification succeeds, does not return anything. If the verification fails, raises an exception. + + :param data: The data, encoded in DER format. + :type data: bytes + + :param content: if specified, the content to verify against the signed message. If the content + is not specified, the function will look for the content in the signed message. Defaults to + None. + :type content: bytes or None + + :param certificate: if specified, a :class:`~cryptography.x509.Certificate` to verify against + the signed message. If None, the function will look for the signer certificate in the signed + message. Defaults to None. + :type certificate: :class:`~cryptography.x509.Certificate` or None + + :raises ValueError: If the recipient certificate does not match any of the signers in the + PKCS7 data. + + :raises ValueError: If no content is specified and no content is found in the PKCS7 data. + + :raises ValueError: If the PKCS7 data is not of the signed data type. + + +.. function:: pkcs7_verify_pem(data, content=None, certificate=None, options=None) + + .. versionadded:: 45.0.0 + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes, serialization + >>> from cryptography.hazmat.primitives.serialization import pkcs7 + >>> cert = x509.load_pem_x509_certificate(verify_cert) + >>> key = serialization.load_pem_private_key(verify_key, None) + >>> signed = pkcs7.PKCS7SignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.PEM, [] + ... ) + >>> pkcs7.pkcs7_verify_pem(signed) + + Deserialize and verify a PEM-encoded PKCS7 signed message. PKCS7 (or S/MIME) has multiple + versions, but this supports a subset of :rfc:`5751`, also known as S/MIME Version 3.2. If the + verification succeeds, does not return anything. If the verification fails, raises an exception. + + :param data: The data, encoded in PEM format. + :type data: bytes + + :param content: if specified, the content to verify against the signed message. If the content + is not specified, the function will look for the content in the signed message. Defaults to + None. + :type content: bytes or None + + :param certificate: if specified, a :class:`~cryptography.x509.Certificate` to verify against + the signed message. If None, the function will look for the signer certificate in the signed + message. Defaults to None. + :type certificate: :class:`~cryptography.x509.Certificate` or None + + :raises ValueError: If the PEM data does not have the PKCS7 tag. + + :raises ValueError: If the recipient certificate does not match any of the signers in the + PKCS7 data. + + :raises ValueError: If no content is specified and no content is found in the PKCS7 data. + + :raises ValueError: If the PKCS7 data is not of the signed data type. + + +.. function:: pkcs7_verify_smime(data, content=None, certificate=None, options=None) + + .. versionadded:: 45.0.0 + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes, serialization + >>> from cryptography.hazmat.primitives.serialization import pkcs7 + >>> cert = x509.load_pem_x509_certificate(verify_cert) + >>> key = serialization.load_pem_private_key(verify_key, None) + >>> signed = pkcs7.PKCS7SignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.SMIME, [] + ... ) + >>> pkcs7.pkcs7_verify_smime(signed) + + Verify a PKCS7 signed message stored in a MIME message, by reading it, extracting the content + (if any) and signature, deserializing the signature and verifying it against the content. PKCS7 + (or S/MIME) has multiple versions, but this supports a subset of :rfc:`5751`, also known as + S/MIME Version 3.2. If the verification succeeds, does not return anything. If the verification + fails, raises an exception. + + :param data: The data, encoded in MIME format. + :type data: bytes + + :param content: if specified, the content to verify against the signed message. If the content + is not specified, the function will look for the content in the MIME message and in the + signature. Defaults to None. + :type content: bytes or None + + :param certificate: if specified, a :class:`~cryptography.x509.Certificate` to verify against + the signed message. If None, the function will look for the signer certificate in the signed + message. Defaults to None. + :type certificate: :class:`~cryptography.x509.Certificate` or None + + :raises ValueError: If the MIME message is not a S/MIME signed message: content type is + different than ``multipart/signed`` or ``application/pkcs7-mime``. + + :raises ValueError: If the MIME message is a malformed ``multipart/signed`` S/MIME message: not + multipart, or multipart with more than 2 parts (content & signature). + + :raises ValueError: If the recipient certificate does not match any of the signers in the + PKCS7 data. + + :raises ValueError: If no content is specified and no content is found in the PKCS7 data. + + :raises ValueError: If the PKCS7 data is not of the signed data type. + .. class:: PKCS7EnvelopeBuilder The PKCS7 envelope builder can create encrypted S/MIME messages, @@ -1633,6 +1799,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, obtain the signer's certificate by other means (for example from a previously signed message). + Serialization Formats ~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/src/cryptography/hazmat/bindings/_rust/pkcs7.pyi index 358b135865a8..48169dd094d1 100644 --- a/src/cryptography/hazmat/bindings/_rust/pkcs7.pyi +++ b/src/cryptography/hazmat/bindings/_rust/pkcs7.pyi @@ -19,11 +19,6 @@ def encrypt_and_serialize( encoding: serialization.Encoding, options: Iterable[pkcs7.PKCS7Options], ) -> bytes: ... -def sign_and_serialize( - builder: pkcs7.PKCS7SignatureBuilder, - encoding: serialization.Encoding, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... def decrypt_der( data: bytes, certificate: x509.Certificate, @@ -42,6 +37,26 @@ def decrypt_smime( private_key: rsa.RSAPrivateKey, options: Iterable[pkcs7.PKCS7Options], ) -> bytes: ... +def sign_and_serialize( + builder: pkcs7.PKCS7SignatureBuilder, + encoding: serialization.Encoding, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def verify_der( + signature: bytes, + content: bytes | None = None, + certificate: x509.Certificate | None = None, +) -> None: ... +def verify_pem( + signature: bytes, + content: bytes | None = None, + certificate: x509.Certificate | None = None, +) -> None: ... +def verify_smime( + signature: bytes, + content: bytes | None = None, + certificate: x509.Certificate | None = None, +) -> None: ... def load_pem_pkcs7_certificates( data: bytes, ) -> list[x509.Certificate]: ... diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index 456dc5b0831c..38b806e356eb 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -21,6 +21,15 @@ algorithms, ) from cryptography.utils import _check_byteslike +from cryptography.x509 import Certificate +from cryptography.x509.oid import ExtendedKeyUsageOID +from cryptography.x509.verification import ( + Criticality, + ExtensionPolicy, + Policy, + PolicyBuilder, + Store, +) load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates @@ -53,6 +62,120 @@ class PKCS7Options(utils.Enum): NoCerts = "Don't embed signer certificate" +def pkcs7_x509_extension_policies() -> tuple[ExtensionPolicy, ExtensionPolicy]: + """ + Gets the default X.509 extension policy for S/MIME, based on RFC 8550. + Visit https://www.rfc-editor.org/rfc/rfc8550#section-4.4 for more info. + """ + # CA policy + ca_policy = ExtensionPolicy.webpki_defaults_ca() + + # EE policy + def _validate_basic_constraints( + policy: Policy, cert: Certificate, bc: x509.BasicConstraints | None + ) -> None: + """ + We check that Certificates used as EE (i.e., the cert used to sign + a PKCS#7/SMIME message) must not have ca=true in their basic + constraints extension. RFC 5280 doesn't impose this requirement, but we + firmly agree about it being best practice. + """ + if bc is not None and bc.ca: + raise ValueError("Basic Constraints CA must be False.") + + def _validate_key_usage( + policy: Policy, cert: Certificate, ku: x509.KeyUsage | None + ) -> None: + """ + Checks that the Key Usage extension, if present, has at least one of + the digital signature or content commitment (formerly non-repudiation) + bits set. + """ + if ( + ku is not None + and not ku.digital_signature + and not ku.content_commitment + ): + raise ValueError( + "Key Usage, if specified, must have at least one of the " + "digital signature or content commitment (formerly non " + "repudiation) bits set." + ) + + def _validate_subject_alternative_name( + policy: Policy, + cert: Certificate, + san: x509.SubjectAlternativeName, + ) -> None: + """ + For each general name in the SAN, for those which are email addresses: + - If it is an RFC822Name, general part must be ascii. + - If it is an OtherName, general part must be non-ascii. + """ + for general_name in san: + if ( + isinstance(general_name, x509.RFC822Name) + and "@" in general_name.value + and not general_name.value.split("@")[0].isascii() + ): + raise ValueError( + f"RFC822Name {general_name.value} contains non-ASCII " + "characters." + ) + if ( + isinstance(general_name, x509.OtherName) + and "@" in general_name.value.decode() + and general_name.value.decode().split("@")[0].isascii() + ): + raise ValueError( + f"OtherName {general_name.value.decode()} is ASCII, " + "so must be stored in RFC822Name." + ) + + def _validate_extended_key_usage( + policy: Policy, cert: Certificate, eku: x509.ExtendedKeyUsage | None + ) -> None: + """ + Checks that the Extended Key Usage extension, if present, + includes either emailProtection or anyExtendedKeyUsage bits. + """ + if ( + eku is not None + and ExtendedKeyUsageOID.EMAIL_PROTECTION not in eku + and ExtendedKeyUsageOID.ANY_EXTENDED_KEY_USAGE not in eku + ): + raise ValueError( + "Extended Key Usage, if specified, must include " + "emailProtection or anyExtendedKeyUsage." + ) + + ee_policy = ( + ExtensionPolicy.webpki_defaults_ee() + .may_be_present( + x509.BasicConstraints, + Criticality.AGNOSTIC, + _validate_basic_constraints, + ) + .may_be_present( + x509.KeyUsage, + Criticality.CRITICAL, + _validate_key_usage, + ) + .require_present( + x509.SubjectAlternativeName, + Criticality.AGNOSTIC, + _validate_subject_alternative_name, + ) + .may_be_present( + x509.ExtendedKeyUsage, + Criticality.AGNOSTIC, + _validate_extended_key_usage, + ) + ) + + return ca_policy, ee_policy + + class PKCS7SignatureBuilder: def __init__( self, @@ -186,6 +309,11 @@ def sign( return rust_pkcs7.sign_and_serialize(self, encoding, options) +pkcs7_verify_der = rust_pkcs7.verify_der +pkcs7_verify_pem = rust_pkcs7.verify_pem +pkcs7_verify_smime = rust_pkcs7.verify_smime + + class PKCS7EnvelopeBuilder: def __init__( self, @@ -358,6 +486,45 @@ def _smime_signed_encode( return fp.getvalue() +def _smime_signed_decode(data: bytes) -> tuple[bytes | None, bytes]: + message = email.message_from_bytes(data) + content_type = message.get_content_type() + if content_type == "multipart/signed": + payload = message.get_payload() + if not isinstance(payload, list): + raise ValueError( + "Malformed multipart/signed message: must be multipart" + ) + assert isinstance(payload[0], email.message.Message), ( + "Malformed multipart/signed message: first part (content) " + "must be a MIME message" + ) + assert isinstance(payload[1], email.message.Message), ( + "Malformed multipart/signed message: second part (signature) " + "must be a MIME message" + ) + return ( + bytes(payload[0].get_payload(decode=True)), + bytes(payload[1].get_payload(decode=True)), + ) + elif content_type == "application/pkcs7-mime": + return None, bytes(message.get_payload(decode=True)) + else: + raise ValueError("Not an S/MIME signed message") + + +def _verify_pkcs7_certificates(certificates: list[x509.Certificate]) -> None: + ca_policy, ee_policy = pkcs7_x509_extension_policies() + verifier = ( + PolicyBuilder() + .store(Store(certificates)) + .extension_policies(ca_policy=ca_policy, ee_policy=ee_policy) + .build_client_verifier() + ) + + verifier.verify(certificates[0], certificates[1:]) + + def _smime_enveloped_encode(data: bytes) -> bytes: m = email.message.Message() m.add_header("MIME-Version", "1.0") diff --git a/src/rust/src/pkcs7.rs b/src/rust/src/pkcs7.rs index 5090ea27619e..2fdd434a4dc4 100644 --- a/src/rust/src/pkcs7.rs +++ b/src/rust/src/pkcs7.rs @@ -25,7 +25,6 @@ use crate::padding::PKCS7UnpaddingContext; use crate::pkcs12::symmetric_encrypt; #[cfg(not(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC)))] use crate::utils::cstr_from_literal; -#[cfg(not(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC)))] use crate::x509::certificate::load_der_x509_certificate; use crate::{exceptions, types, x509}; @@ -201,6 +200,7 @@ fn decrypt_smime<'p>( decrypt_der(py, data, certificate, private_key, options) } + #[pyo3::pyfunction] fn decrypt_pem<'p>( py: pyo3::Python<'p>, @@ -672,6 +672,208 @@ fn compute_pkcs7_signature_algorithm<'p>( } } +#[pyo3::pyfunction] +#[pyo3(signature = (signature, content = None, certificate = None))] +fn verify_smime<'p>( + py: pyo3::Python<'p>, + signature: &[u8], + content: Option<&[u8]>, + certificate: Option>, +) -> CryptographyResult<()> { + // Parse the email + let py_content_and_signature = types::SMIME_SIGNED_DECODE.get(py)?.call1((signature,))?; + + // Extract the signature + let py_signature = py_content_and_signature.get_item(1)?; + let signature = py_signature.extract()?; + + // Extract the content: if content is specified, use it, otherwise use the content from the + // email. It can be None (opaque signing) or bytes (clear signing with multipart/signed email). + // RFC5751 specified that receiving agents MUST be able to handle both cases. + let py_content = py_content_and_signature.get_item(0)?; + let content = match content { + Some(data) => Some(data), + None => { + if !py_content.is_none() { + Some(py_content.extract()?) + } else { + None + } + } + }; + + verify_der(py, signature, content, certificate) +} + +#[pyo3::pyfunction] +#[pyo3(signature = (signature, content = None, certificate = None))] +fn verify_pem<'p>( + py: pyo3::Python<'p>, + signature: &[u8], + content: Option<&[u8]>, + certificate: Option>, +) -> CryptographyResult<()> { + let pem_str = std::str::from_utf8(signature) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("Invalid PEM data"))?; + let pem = pem::parse(pem_str) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("Failed to parse PEM data"))?; + + // Raise error if the PEM tag is not PKCS7 + if pem.tag() != "PKCS7" { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "The provided PEM data does not have the PKCS7 tag.", + ), + )); + } + + verify_der(py, &pem.into_contents(), content, certificate) +} + +#[pyo3::pyfunction] +#[pyo3(signature = (signature, content = None, certificate = None))] +fn verify_der<'p>( + py: pyo3::Python<'p>, + signature: &[u8], + content: Option<&[u8]>, + certificate: Option>, +) -> CryptographyResult<()> { + // Verify the data + let content_info = asn1::parse_single::>(signature)?; + match content_info.content { + pkcs7::Content::SignedData(signed_data) => { + // Extract signed data + let signed_data = signed_data.into_inner(); + + // Extract the signer certificate: either from given value, or from signed data + let certificate = match certificate { + Some(cert) => cert, + None => { + let certificates = signed_data.certificates; + match certificates { + Some(certificates) => { + let mut certificates = certificates.unwrap_read().clone(); + match certificates.next() { + Some(cert) => { + let cert_bytes = + pyo3::types::PyBytes::new(py, &asn1::write_single(&cert)?) + .unbind(); + let py_cert = load_der_x509_certificate(py, cert_bytes, None)?; + pyo3::Bound::new(py, py_cert)? + } + None => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "The PKCS7 data has an empty certificates attributes.", + ), + )); + } + } + } + None => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "The PKCS7 data does not contain any certificate.", + ), + )); + } + } + } + }; + + // Get recipients, and find the one matching with the signer infos (if any) + // TODO: what to do for multiple certificates? + let mut signer_infos = signed_data.signer_infos.unwrap_read().clone(); + let signer_certificate = certificate.get().raw.borrow_dependent(); + let signer_serial_number = signer_certificate.tbs_cert.serial; + let signer_issuer = signer_certificate.tbs_cert.issuer.clone(); + let found_signer_info = signer_infos.find(|info| { + info.issuer_and_serial_number.serial_number == signer_serial_number + && info.issuer_and_serial_number.issuer == signer_issuer + }); + + // Raise error when no signer is found + let signer_info = match found_signer_info { + Some(info) => info, + None => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "No signer found that matches the given certificate.", + ), + )); + } + }; + + // Prepare the content: try to use the authenticated attributes, then the content stored + // in the signed data, then the provided content. If None of these are available, raise + // an error. TODO: what should the order be? + let data = match signer_info.authenticated_attributes { + Some(attrs) => Cow::Owned(asn1::write_single(&attrs)?), + None => match content { + Some(data) => Cow::Borrowed(data), + None => match signed_data.content_info.content { + pkcs7::Content::Data(Some(data)) => Cow::Borrowed(data.into_inner()), + _ => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "No content stored in the signature or provided.", + ), + )); + } + }, + }, + }; + + // For RSA signatures (with no PSS padding), the OID is always the same no matter the + // digest algorithm. We need to modify the algorithm identifier to add the hash + // algorithm information. We are checking for RSA-256, which the S/MIME v3.2 RFC + // specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.2) + let signature_algorithm = match signer_info.digest_encryption_algorithm.oid() { + &oid::RSA_OID => match signer_info.digest_algorithm.oid() { + &oid::SHA256_OID => common::AlgorithmIdentifier { + oid: asn1::DefinedByMarker::marker(), + params: common::AlgorithmParameters::RsaWithSha256(Some(())), + }, + _ => { + return Err(CryptographyError::from( + exceptions::UnsupportedAlgorithm::new_err(( + "Only SHA-256 is currently supported for content verification with RSA.", + exceptions::Reasons::UNSUPPORTED_SERIALIZATION, + )), + )) + } + }, + _ => signer_info.digest_encryption_algorithm, + }; + + // Verify the signature + x509::sign::verify_signature_with_signature_algorithm( + py, + certificate.call_method0(pyo3::intern!(py, "public_key"))?, + &signature_algorithm, + signer_info.encrypted_digest, + &data, + )?; + + // Verify the certificate + let certificates = pyo3::types::PyList::empty(py); + certificates.append(certificate)?; + types::VERIFY_PKCS7_CERTIFICATES + .get(py)? + .call1((certificates,))?; + } + _ => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "The PKCS7 data is not a SignedData structure.", + ), + )); + } + }; + + Ok(()) +} + fn smime_canonicalize(data: &[u8], text_mode: bool) -> (Cow<'_, [u8]>, Cow<'_, [u8]>) { let mut new_data_with_header = vec![]; let mut new_data_without_header = vec![]; @@ -811,7 +1013,7 @@ pub(crate) mod pkcs7_mod { use super::{ decrypt_der, decrypt_pem, decrypt_smime, encrypt_and_serialize, load_der_pkcs7_certificates, load_pem_pkcs7_certificates, serialize_certificates, - sign_and_serialize, + sign_and_serialize, verify_der, verify_pem, verify_smime, }; } diff --git a/src/rust/src/types.rs b/src/rust/src/types.rs index 304d7b421d70..c06410d5deb4 100644 --- a/src/rust/src/types.rs +++ b/src/rust/src/types.rs @@ -374,6 +374,16 @@ pub static SMIME_SIGNED_ENCODE: LazyPyImport = LazyPyImport::new( &["_smime_signed_encode"], ); +pub static SMIME_SIGNED_DECODE: LazyPyImport = LazyPyImport::new( + "cryptography.hazmat.primitives.serialization.pkcs7", + &["_smime_signed_decode"], +); + +pub static VERIFY_PKCS7_CERTIFICATES: LazyPyImport = LazyPyImport::new( + "cryptography.hazmat.primitives.serialization.pkcs7", + &["_verify_pkcs7_certificates"], +); + pub static PKCS12KEYANDCERTIFICATES: LazyPyImport = LazyPyImport::new( "cryptography.hazmat.primitives.serialization.pkcs12", &["PKCS12KeyAndCertificates"], diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index 1496a23e1b2e..436c1c34d513 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -15,9 +15,24 @@ from cryptography.exceptions import _Reasons from cryptography.hazmat.bindings._rust import test_support from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ed25519, padding, rsa +from cryptography.hazmat.primitives.asymmetric import ( + ed25519, + padding, + rsa, + types, +) from cryptography.hazmat.primitives.ciphers import algorithms from cryptography.hazmat.primitives.serialization import pkcs7 +from cryptography.x509.oid import ( + ExtendedKeyUsageOID, + ExtensionOID, + ObjectIdentifier, +) +from cryptography.x509.verification import ( + PolicyBuilder, + Store, + VerificationError, +) from tests.x509.test_x509 import _generate_ca_and_leaf from ...hazmat.primitives.fixtures_rsa import ( @@ -125,20 +140,153 @@ def test_load_pkcs7_empty_certificates(self): def _load_cert_key(): key = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "ca_key.pem"), + os.path.join("pkcs7", "ca_key.pem"), lambda pemfile: serialization.load_pem_private_key( pemfile.read(), None, unsafe_skip_rsa_key_validation=True ), mode="rb", ) cert = load_vectors_from_file( - os.path.join("x509", "custom", "ca", "ca.pem"), + os.path.join("pkcs7", "ca.pem"), loader=lambda pemfile: x509.load_pem_x509_certificate(pemfile.read()), mode="rb", ) return cert, key +class TestPKCS7VerifyCertificate: + @staticmethod + def build_pkcs7_certificate( + ca: bool = False, + digital_signature: bool = True, + usages: typing.Optional[typing.List[ObjectIdentifier]] = None, + ) -> x509.Certificate: + """ + This static method is a helper to build certificates allowing us + to test all cases in PKCS#7 certificate verification. + """ + # Load the standard certificate and private key + certificate, private_key = _load_cert_key() + + # Basic certificate builder + certificate_builder = ( + x509.CertificateBuilder() + .serial_number(certificate.serial_number) + .subject_name(certificate.subject) + .issuer_name(certificate.issuer) + .public_key(private_key.public_key()) + .not_valid_before(certificate.not_valid_before) + .not_valid_after(certificate.not_valid_after) + ) + + # Add AuthorityKeyIdentifier extension + aki = certificate.extensions.get_extension_for_oid( + ExtensionOID.AUTHORITY_KEY_IDENTIFIER + ) + certificate_builder = certificate_builder.add_extension( + aki.value, critical=False + ) + + # Add SubjectAlternativeName extension + san = certificate.extensions.get_extension_for_oid( + ExtensionOID.SUBJECT_ALTERNATIVE_NAME + ) + certificate_builder = certificate_builder.add_extension( + san.value, critical=True + ) + + # Add BasicConstraints extension + bc_extension = x509.BasicConstraints(ca=ca, path_length=None) + certificate_builder = certificate_builder.add_extension( + bc_extension, False + ) + + # Add KeyUsage extension + ku_extension = x509.KeyUsage( + digital_signature=digital_signature, + content_commitment=False, + key_encipherment=True, + data_encipherment=True, + key_agreement=True, + key_cert_sign=True, + crl_sign=True, + encipher_only=False, + decipher_only=False, + ) + certificate_builder = certificate_builder.add_extension( + ku_extension, True + ) + + # Add valid ExtendedKeyUsage extension + usages = usages or [ExtendedKeyUsageOID.EMAIL_PROTECTION] + certificate_builder = certificate_builder.add_extension( + x509.ExtendedKeyUsage(usages), True + ) + + # Build the certificate + return certificate_builder.sign( + private_key, certificate.signature_hash_algorithm, None + ) + + def test_verify_pkcs7_certificate(self): + # Prepare the parameters + certificate = self.build_pkcs7_certificate() + ca_policy, ee_policy = pkcs7.pkcs7_x509_extension_policies() + + # Verify the certificate + verifier = ( + PolicyBuilder() + .store(Store([certificate])) + .extension_policies(ca_policy=ca_policy, ee_policy=ee_policy) + .build_client_verifier() + ) + verifier.verify(certificate, []) + + @pytest.mark.parametrize( + "arguments", + [ + {"ca": True}, + {"digital_signature": False}, + {"usages": [ExtendedKeyUsageOID.CLIENT_AUTH]}, + ], + ) + def test_verify_invalid_pkcs7_certificate(self, arguments: dict): + # Prepare the parameters + certificate = self.build_pkcs7_certificate(**arguments) + + # Verify the certificate + self.verify_invalid_pkcs7_certificate(certificate) + + @staticmethod + def verify_invalid_pkcs7_certificate(certificate: x509.Certificate): + ca_policy, ee_policy = pkcs7.pkcs7_x509_extension_policies() + verifier = ( + PolicyBuilder() + .store(Store([certificate])) + .extension_policies(ca_policy=ca_policy, ee_policy=ee_policy) + .build_client_verifier() + ) + + with pytest.raises(VerificationError): + verifier.verify(certificate, []) + + @pytest.mark.parametrize( + "filename", ["ca_non_ascii_san.pem", "ca_ascii_san.pem"] + ) + def test_verify_pkcs7_certificate_wrong_san(self, filename): + # Read a certificate with an invalid SAN + pkcs7_certificate = load_vectors_from_file( + os.path.join("pkcs7", filename), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + + # Verify the certificate + self.verify_invalid_pkcs7_certificate(pkcs7_certificate) + + @pytest.mark.supported( only_if=lambda backend: backend.pkcs7_supported(), skip_message="Requires OpenSSL with PKCS7 support", @@ -860,6 +1008,286 @@ def test_add_multiple_additional_certs(self, backend): ) +@pytest.mark.supported( + only_if=lambda backend: backend.pkcs7_supported(), + skip_message="Requires OpenSSL with PKCS7 support", +) +class TestPKCS7Verify: + @pytest.fixture(name="data") + def fixture_data(self, backend) -> bytes: + return b"Hello world!" + + @pytest.fixture(name="certificate") + def fixture_certificate(self, backend) -> x509.Certificate: + return load_vectors_from_file( + os.path.join("pkcs7", "ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + + @pytest.fixture(name="private_key") + def fixture_private_key(self, backend) -> types.PrivateKeyTypes: + return load_vectors_from_file( + os.path.join("pkcs7", "ca_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None, unsafe_skip_rsa_key_validation=True + ), + mode="rb", + ) + + def test_not_a_cert(self, backend): + with pytest.raises(TypeError): + pkcs7.pkcs7_verify_der(b"", certificate=b"wrong_type") # type: ignore[arg-type] + + @pytest.mark.parametrize( + "signing_options", + [ + [], + [pkcs7.PKCS7Options.NoAttributes], + ], + ) + def test_pkcs7_verify_der( + self, backend, data, certificate, private_key, signing_options + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signature = builder.sign(serialization.Encoding.DER, signing_options) + + # Verification + pkcs7.pkcs7_verify_der(signature) + + def test_pkcs7_verify_der_with_certificate( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + options = [pkcs7.PKCS7Options.NoCerts] + signature = builder.sign(serialization.Encoding.DER, options) + + # Verification + pkcs7.pkcs7_verify_der(signature, certificate=certificate) + + def test_pkcs7_verify_der_empty_certificates(self, backend): + # Getting a signature without certificates: empty list, not None + signature_empty_certificates = load_vectors_from_file( + os.path.join("pkcs7", "signature-empty-certs.der"), + loader=lambda derfile: derfile.read(), + mode="rb", + ) + + # Verification + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_der(signature_empty_certificates) + + def test_pkcs7_verify_der_no_certificates( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + options = [pkcs7.PKCS7Options.NoCerts] + signature = builder.sign(serialization.Encoding.DER, options) + + # Verification + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_der(signature) + + def test_pkcs7_verify_der_with_content( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.NoAttributes, + pkcs7.PKCS7Options.DetachedSignature, + ] + signature = builder.sign(serialization.Encoding.DER, options) + + # Verification + pkcs7.pkcs7_verify_der(signature, content=data) + + def test_pkcs7_verify_der_no_content( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.NoAttributes, + pkcs7.PKCS7Options.DetachedSignature, + ] + signature = builder.sign(serialization.Encoding.DER, options) + + # Verification + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_der(signature) + + def test_pkcs7_verify_der_ecdsa_certificate(self, backend, data): + # Getting an ECDSA certificate + certificate, private_key = _load_cert_key() + + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signature = builder.sign(serialization.Encoding.DER, []) + + # Verification with another certificate + pkcs7.pkcs7_verify_der(signature) + + def test_pkcs7_verify_invalid_signature( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.NoAttributes, + pkcs7.PKCS7Options.DetachedSignature, + ] + signature = builder.sign(serialization.Encoding.DER, options) + + # Verification + with pytest.raises(exceptions.InvalidSignature): + pkcs7.pkcs7_verify_der(signature, content=b"Different") + + def test_pkcs7_verify_der_wrong_certificate( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signature = builder.sign(serialization.Encoding.DER, []) + + # Verification with another certificate + rsa_certificate, _ = _load_rsa_cert_key() + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_der(signature, certificate=rsa_certificate) + + def test_pkcs7_verify_der_unsupported_rsa_digest_algorithm( + self, backend, data + ): + certificate, private_key = _load_rsa_cert_key() + + # Signature with an unsupported digest algorithm + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA384()) + ) + signature = builder.sign(serialization.Encoding.DER, []) + + # Verification + with pytest.raises(exceptions.UnsupportedAlgorithm): + pkcs7.pkcs7_verify_der(signature) + + def test_pkcs7_verify_pem(self, backend, data, certificate, private_key): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signature = builder.sign(serialization.Encoding.PEM, []) + + # Verification + pkcs7.pkcs7_verify_pem(signature, data, certificate) + + def test_pkcs7_verify_pem_with_wrong_tag(self, backend, data, certificate): + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_pem( + certificate.public_bytes(serialization.Encoding.PEM) + ) + + def test_pkcs7_verify_pem_not_signed(self, backend, data, certificate): + # Getting some enveloped data + enveloped = load_vectors_from_file( + os.path.join("pkcs7", "enveloped.pem"), + loader=lambda pemfile: pemfile.read(), + mode="rb", + ) + + # Verification + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_pem(enveloped) + + def test_pkcs7_verify_smime(self, backend, data, certificate, private_key): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signed = builder.sign(serialization.Encoding.SMIME, []) + + # Verification + pkcs7.pkcs7_verify_smime(signed) + + def test_pkcs7_verify_smime_with_content( + self, backend, data, certificate, private_key + ): + # Signature + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(certificate, private_key, hashes.SHA256()) + ) + signed = builder.sign(serialization.Encoding.SMIME, []) + + # Verification + pkcs7.pkcs7_verify_smime(signed, content=data) + + def test_pkcs7_verify_smime_opaque_signing(self, backend): + # Signature + signed = load_vectors_from_file( + os.path.join("pkcs7", "signed-opaque.msg"), + loader=lambda file: file.read(), + mode="rb", + ) + + # Verification + pkcs7.pkcs7_verify_smime(signed) + + @pytest.mark.parametrize( + "signature", + [ + b"Content-Type: text/plain;\nHello world!", + b"Content-Type: multipart/signed;\nHello world!", + ], + ) + def test_pkcs7_verify_smime_wrong_format(self, backend, signature): + with pytest.raises(ValueError): + pkcs7.pkcs7_verify_smime(signature) + + def _load_rsa_cert_key(): key = load_vectors_from_file( os.path.join("x509", "custom", "ca", "rsa_key.pem"), diff --git a/vectors/cryptography_vectors/pkcs7/ca.pem b/vectors/cryptography_vectors/pkcs7/ca.pem new file mode 100644 index 000000000000..d11b0ec59b35 --- /dev/null +++ b/vectors/cryptography_vectors/pkcs7/ca.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhjCCASygAwIBAgICAwkwCgYIKoZIzj0EAwIwJzELMAkGA1UEBhMCVVMxGDAW +BgNVBAMMD2NyeXB0b2dyYXBoeSBDQTAgFw0xNzAxMDEwMTAwMDBaGA8yMTAwMDEw +MTAwMDAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBD +QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N +2CxSJE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjRjBE +MCEGA1UdEQEB/wQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wHwYDVR0jBBgwFoAU +/Ou02BLyyT2Zwzxn9H03feYT7fowCgYIKoZIzj0EAwIDSAAwRQIgUwIdC0Emkd6f +17DeOXTlmTAhwSDJ2FTuyHESwei7wJcCIQCnr9NpBxbtJfEzxHGGyd7PxgpOLi5u +rk+8QfzGMmg/fw== +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/pkcs7/ca_ascii_san.pem b/vectors/cryptography_vectors/pkcs7/ca_ascii_san.pem new file mode 100644 index 000000000000..7e184abcbe3c --- /dev/null +++ b/vectors/cryptography_vectors/pkcs7/ca_ascii_san.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCAsSgAwIBAgIUGJw032ss5tmRmaY8x41pL5lqqRYwDQYJKoZIhvcNAQEL +BQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xFTATBgNVBAoMDEV4YW1wbGUgQ29ycDEWMBQGA1UECwwN +SVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwNjA5MTg0 +NzQ1WhcNMjYwNjA5MTg0NzQ1WjB/MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs +aWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMGA1UECgwMRXhhbXBs +ZSBDb3JwMRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxl +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALLWXuy3atOjhb8g +fa5AC5me9PqRqcqV63e+NIe8IaKioCM5Sl+3jhKb5DdPIjfQYbHbwPtY+rFSP364 +dBZoJpCDG4gcD6H3eS5JGc8Uz62l+oBNuFoU3EZiUNMF0k17vs/6CGeyt53+D9DJ +PG6Wv87nAAoK97r1rLdC8Of97QpUV/st+YDP7/LOH8CxJZOnbiUdekzo0dCQkk7n +17hJCYN1Y98VrlZFY25ny2TURUgK7lIjduEUb0dugYiepjzp7ZV8184kpAD/PtLT +czA1S8e6kySd5wbJSFcKxrk/j/cccUGLMyKPlMZgsHZUm/2DOLWLljxbEjCOxb1G +8+EpR9kCAwEAAaNQME4wLQYDVR0RBCYwJKAiBggrBgEFBQcICaAWDBRyZXRvdXJu +ZUBleGFtcGxlLmNvbTAdBgNVHQ4EFgQUm24AOQAmOInCPZPDUagXXw+BEl0wDQYJ +KoZIhvcNAQELBQADggEBAGgLqsx27sS28t1okxT1MU6QhfAn/Yw07Nhk3cpNKGnh +edrPPTXvJc05qHuQIqOiFIJ4SojbQ2+bVZwo7V3Jhspx9T+Gkb/Dn3rHpAfOXuaJ +RqJ777Cor2seAKv07jerGnEULYW8JcezZDGbv6ViC0oEgazwTzahfynrUMJ2DJRX +tnNdczDsGw+DVMvOBzcSE/aEzhd4ghgVq5aFS05wzhN/fTWKiN4tpEAG6y95gU73 +29O3y1W3dLjblTZJvXNtgCjMT6R3OVeWAsqyXDprFrZWZucCj8opIxRf6jpZlRfJ +qW+57pkefhg3q4MFjn08BOKpYwOdRouGE4l96dGBDwM= +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/pkcs7/ca_key.pem b/vectors/cryptography_vectors/pkcs7/ca_key.pem new file mode 100644 index 000000000000..2fb5394195cb --- /dev/null +++ b/vectors/cryptography_vectors/pkcs7/ca_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe +jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs +UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF +-----END PRIVATE KEY----- diff --git a/vectors/cryptography_vectors/pkcs7/ca_non_ascii_san.pem b/vectors/cryptography_vectors/pkcs7/ca_non_ascii_san.pem new file mode 100644 index 000000000000..f590d881e68e --- /dev/null +++ b/vectors/cryptography_vectors/pkcs7/ca_non_ascii_san.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIUAX/xKTtlMllrK5ng0+OkmnxxIugwDQYJKoZIhvcNAQEL +BQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xFTATBgNVBAoMDEV4YW1wbGUgQ29ycDEWMBQGA1UECwwN +SVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwNjA5MTgw +NzE4WhcNMjYwNjA5MTgwNzE4WjB/MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs +aWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMGA1UECgwMRXhhbXBs +ZSBDb3JwMRYwFAYDVQQLDA1JVCBEZXBhcnRtZW50MRQwEgYDVQQDDAtleGFtcGxl +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOxyV/ZsaGn7dOcZ +6ODFcnmwjPCKRASFeDtOMYoGrlALb9zA+UMuMB63dTZ8ofWsDgLLGhw86njfSYad +RslOw8Bki9lKiS1RhS/RbnDSBWB2wJzniyFn/qI2F93WbgqHMOnzzJcAkc/YPU0T +iyvNpjD3Q/xObcp7ouBJJmFSvLybSTJtFrVzkpIbDZYrn0KyKtgTCPc/r9D04u+u +scSACvTRjePsEZIgRkVgfVpdBmy1KeJmx2NqS8Yev+y+0e9q3t8Ga/j/CnPFXlEl +iBHciFtkKdd2HrPLJMXBKhMn2KagLJSSdABNApi8qULIpOnrEE8FepKCzkptFyS1 +5g0H3u0CAwEAAaNDMEEwIAYDVR0RBBkwF4EVcmV0b3VybsOpQGV4YW1wbGUuY29t +MB0GA1UdDgQWBBTthtqdM0IoehNymXnqMPX1joF1LzANBgkqhkiG9w0BAQsFAAOC +AQEApQZ3vOuBgNg1U26c4l0VSCU5q73Lecbgjc42AhEp9FyP7ratj4MyH7RGr4io +vl0wWROFBnzliW5ZA8CP3Ux4AbqgtxcFPBRHACjmrpoSFHmW7bpzRnqwJKwXsOGJ +ZhjA/2o91lEJr0UNhpvSGyR+xCkuvw83mvM1rmE19yNMElv96x/DPVQV2ocsffOb +kS7pIpvXX3pSIj7Up0Xrz+bSyhJlsO3sO5bREshyvuiRivm9AjBVRY/BtbFY6DcV +9javEitCw93BgImIs0CXGpZUrvphX8muWVct5xpKj64/Yo0hIYystX+xVl3EjTRf +B7pH2DE+cXg99p7L6RoYtlOeRA== +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/pkcs7/signature-empty-certs.der b/vectors/cryptography_vectors/pkcs7/signature-empty-certs.der new file mode 100644 index 000000000000..05698d83a15d Binary files /dev/null and b/vectors/cryptography_vectors/pkcs7/signature-empty-certs.der differ diff --git a/vectors/cryptography_vectors/pkcs7/signed-opaque.msg b/vectors/cryptography_vectors/pkcs7/signed-opaque.msg new file mode 100644 index 000000000000..29a805ca186f --- /dev/null +++ b/vectors/cryptography_vectors/pkcs7/signed-opaque.msg @@ -0,0 +1,40 @@ +MIME-Version: 1.0 +Content-Type: application/pkcs7-mime; smime-type=signed-data; name=smime.p7m +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=smime.p7m + +MIIGagYJKoZIhvcNAQcCoIIGWzCCBlcCAQExDzANBglghkgBZQMEAgEFADALBgkq +hkiG9w0BBwGgggP7MIID9zCCAt+gAwIBAgIQIxMA+XhyS9Ou0qAc0zPyVTANBgkq +hkiG9w0BAQsFADANMQswCQYDVQQDDAJDQTAeFw0yNTAxMDUxMDQ4MjhaFw0yNjAx +MDUxMDQ4MjhaMCUxIzAhBgkqhkiG9w0BCQEWFGRlbW8xQHRyaXNvZnQuY29tLnBs +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt0WRzh5y+QmEUjCm+iHX +ZLrstOSSEhiEcUre3L8zkuGYVLCKBEvmaHQI7uCu/xdqEht6/wEBCiK+KLdGDVrD +4v3A7TnmHzzhvqCsBTL/EmnD3ZMAJVYv4uEBaFpFPSYnPswd353E6KRkFYR4RmFj +G9xLTayHXOKqCF6dHd3uVR7NSs98uhcSYRV7g4NdjmaDj8kz5HeRMfr/uqbcriJ9 +tu/ljFBWYSwPeiNYnYhaOBLpUhZckyjFDfC+UpwOBPlkK7J047urvzG21xCtVU9D +MHtXMkXYe/C+WSm1MRYtgcsOTxpGf+ujceltI2/+IUhWxr5ys7m+xM1jYaM4O1Pw +0QIDAQABo4IBOTCCATUwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQduUy7zqv6 +z3uk4fJeifohSntD2TAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8vY2EudHJpc29m +dC5jb20ucGwvY3JsMGYGCCsGAQUFBwEBBFowWDArBggrBgEFBQcwAoYfaHR0cDov +L2NhLnRyaXNvZnQuY29tLnBsL2NhY2VydDApBggrBgEFBQcwAYYdaHR0cDovL2Nh +LnRyaXNvZnQuY29tLnBsL29jc3AwHwYDVR0RBBgwFoEUZGVtbzFAdHJpc29mdC5j +b20ucGwwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMB0GA1UdDgQWBBT0 +/QFDFX/CCMsX356GImiWwPYxjDAOBgNVHQ8BAf8EBAMCA+gwDQYJKoZIhvcNAQEL +BQADggEBAL3IiscaIqoFBLMox3cIhCANWO/U1eOvjDjfM/tOHn+6jci/pL/ZHgdR +tqCCiaCKtJED/f/9NFUKqcSZ9+vzW0RWLJxHgIvCSjLpoM06XClSlxjVnv62Hb1N +C4FfDfnzyG+DZHusnz/MQuXNwHntA6+JyB/HWHUie2ierQYH2mEN1XIJm5luSGwt +uGaWfNz/w324ukcVpMd3CbEOZqqfSYGWUHOVG90/OMSfKA/I0hia8Yij0X4Ny+b+ +bLnHaoozZwJ/UqBl9ptbfiOOuFXJP7gt547Rp6+2C0XGJM+le0EYlUzbWE6UWgxa +IRp5uc8HnUd5e4lXbr+Ixxcl3WHckkkxggIzMIICLwIBATAhMA0xCzAJBgNVBAMM +AkNBAhAjEwD5eHJL067SoBzTM/JVMA0GCWCGSAFlAwQCAQUAoIHkMBgGCSqGSIb3 +DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI1MDExNTEwMTY0Mlow +LwYJKoZIhvcNAQkEMSIEIMBTXkvit5/9kykTBUNr+IkxTko/rsBez/y7ffMa2eUa +MHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjALBglg +hkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3DQMC +AgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIBAG0i +Crj4XL+zSIeEf/xGL3sQ4V1EdgjTXSCNgcUYyDVv/bsv9+C4gt5kwbdGE+zAacwo +MGn64jzkTft5SodG8tJ8Y/NM3+G6NDLzBVS89TYXl2/UFlqfKS8Te8lU+Gg+/5J1 +e04ulJR0UXIFVMHQs/Dn8/koUYOrDhcl9ULohhbMArqdy8BAuaEcOn2F+1ORF85G +T1Ks16qVFaSZIsKulsrMiIdTOu+ww78VTneQH7ITVqE04w8x6yQohLBl3jHAT/lA +RufRRBEjnSkzccM2tNjOXAKGqaoAAZz9IqR5HLR5NGbHzapQo8Ft7Ri0//aZYBZg +aqBoycKshpupsMECEPU= \ No newline at end of file