Skip to content

Commit

Permalink
Test S/MIME encryption by decrypting and comparing with plaintext
Browse files Browse the repository at this point in the history
  • Loading branch information
facutuesca committed Jul 1, 2024
1 parent de00dad commit c37582e
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/_cffi_src/openssl/pkcs7.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
https://github.com/pyca/cryptography/issues/5433 */
int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *,
BIO *, int);
int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int);
PKCS7 *SMIME_read_PKCS7(BIO *, BIO **);
"""

Expand All @@ -29,6 +30,7 @@
int (*PKCS7_verify)(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *,
BIO *, int) = NULL;
int (*PKCS7_decrypt)(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int) = NULL;
PKCS7 *(*SMIME_read_PKCS7)(BIO *, BIO **) = NULL;
#else
static const long Cryptography_HAS_PKCS7_FUNCS = 1;
Expand Down
2 changes: 1 addition & 1 deletion src/cryptography/hazmat/bindings/openssl/_conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def cryptography_has_ssl_cookie() -> list[str]:
def cryptography_has_pkcs7_funcs() -> list[str]:
return [
"PKCS7_verify",
"SMIME_read_PKCS7",
"PKCS7_decrypt" "SMIME_read_PKCS7",
]


Expand Down
106 changes: 103 additions & 3 deletions tests/hazmat/primitives/test_pkcs7.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,41 @@ def _pkcs7_verify(encoding, sig, msg, certs, options, backend):
backend._consume_errors()


def _pkcs7_decrypt(encoding, msg, pkey, cert_recipient, options, backend):
msg_bio = backend._bytes_to_bio(msg)
if encoding is serialization.Encoding.DER:
p7 = backend._lib.d2i_PKCS7_bio(msg_bio.bio, backend._ffi.NULL)
elif encoding is serialization.Encoding.PEM:
p7 = backend._lib.PEM_read_bio_PKCS7(
msg_bio.bio,
backend._ffi.NULL,
backend._ffi.NULL,
backend._ffi.NULL,
)
else:
p7 = backend._lib.SMIME_read_PKCS7(msg_bio.bio, backend._ffi.NULL)
backend.openssl_assert(p7 != backend._ffi.NULL)
p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free)
flags = 0
for option in options:
if option is pkcs7.PKCS7Options.Text:
flags |= backend._lib.PKCS7_TEXT

ossl_cert = backend._cert2ossl(cert_recipient)
ossl_pkey = backend._key2ossl(pkey)
# libressl 3.7.0 has a bug when NULL is passed as an `out_bio`. Work
# around it for now.
out_bio = backend._create_mem_bio_gc()
res = backend._lib.PKCS7_decrypt(p7, ossl_pkey, ossl_cert, out_bio, flags)
backend.openssl_assert(res == 1)
# OpenSSL 3.0 leaves a random bio error on the stack:
# https://github.com/openssl/openssl/issues/16681
if rust_openssl.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
backend._consume_errors()

return backend._read_mem_bio(out_bio)


def _load_cert_key():
key = load_vectors_from_file(
os.path.join("x509", "custom", "ca", "ca_key.pem"),
Expand Down Expand Up @@ -1009,7 +1044,7 @@ def test_encrypt_invalid_encryption_options(
)
def test_smime_encrypt_smime_encoding(self, backend, options):
data = b"hello world\n"
cert, _ = _load_rsa_cert_key()
cert, private_key = _load_rsa_cert_key()
builder = (
pkcs7.PKCS7EnvelopeBuilder().set_data(data).add_recipient(cert)
)
Expand Down Expand Up @@ -1038,6 +1073,22 @@ def test_smime_encrypt_smime_encoding(self, backend, options):
b"\x20\x43\x41"
) in payload

decrypted_bytes = _pkcs7_decrypt(
serialization.Encoding.SMIME,
enveloped,
private_key,
cert,
options,
backend,
)
# New lines are canonicalized to '\r\n' when not using Binary
expected_data = (
data
if pkcs7.PKCS7Options.Binary in options
else data.replace(b"\n", b"\r\n")
)
assert decrypted_bytes == expected_data

@pytest.mark.parametrize(
"options",
[
Expand All @@ -1047,7 +1098,7 @@ def test_smime_encrypt_smime_encoding(self, backend, options):
)
def test_smime_encrypt_der_encoding(self, backend, options):
data = b"hello world\n"
cert, _ = _load_rsa_cert_key()
cert, private_key = _load_rsa_cert_key()
builder = (
pkcs7.PKCS7EnvelopeBuilder().set_data(data).add_recipient(cert)
)
Expand All @@ -1065,9 +1116,58 @@ def test_smime_encrypt_der_encoding(self, backend, options):
b"\x20\x43\x41"
) in enveloped

decrypted_bytes = _pkcs7_decrypt(
serialization.Encoding.DER,
enveloped,
private_key,
cert,
options,
backend,
)
# New lines are canonicalized to '\r\n' when not using Binary
expected_data = (
data
if pkcs7.PKCS7Options.Binary in options
else data.replace(b"\n", b"\r\n")
)
assert decrypted_bytes == expected_data

@pytest.mark.parametrize(
"options",
[
[pkcs7.PKCS7Options.Text],
[pkcs7.PKCS7Options.Binary],
],
)
def test_smime_encrypt_pem_encoding(self, backend, options):
data = b"hello world\n"
cert, private_key = _load_rsa_cert_key()
builder = (
pkcs7.PKCS7EnvelopeBuilder().set_data(data).add_recipient(cert)
)
enveloped = builder.encrypt(serialization.Encoding.PEM, options)
with open("msg.p7m", "wb") as f:
f.write(enveloped)

decrypted_bytes = _pkcs7_decrypt(
serialization.Encoding.PEM,
enveloped,
private_key,
cert,
options,
backend,
)
# New lines are canonicalized to '\r\n' when not using Binary
expected_data = (
data
if pkcs7.PKCS7Options.Binary in options
else data.replace(b"\n", b"\r\n")
)
assert decrypted_bytes == expected_data

def test_smime_encrypt_multiple_recipients(self, backend):
data = b"hello world\n"
cert, _ = _load_rsa_cert_key()
cert, private_key = _load_rsa_cert_key()
builder = (
pkcs7.PKCS7EnvelopeBuilder()
.set_data(data)
Expand Down

0 comments on commit c37582e

Please sign in to comment.