Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions pypdf/_crypt_providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@
rc4_decrypt,
rc4_encrypt,
)
from pypdf._utils import Version

if Version(crypt_provider[1]) <= Version("3.0"):
# This is due to the backend parameter being required back then:
# https://cryptography.io/en/latest/changelog/#v3-1
raise ImportError("cryptography<=3.0 is not supported") # pragma: no cover
except ImportError:
try:
from pypdf._crypt_providers._pycryptodome import ( # type: ignore
Expand Down
11 changes: 10 additions & 1 deletion pypdf/_crypt_providers/_cryptography.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,18 @@ def decrypt(self, data: bytes, *, strict: bool = True) -> bytes:
if not data:
return data

if not strict and len(data) % 16 != 0:
logger_warning("Adding missing padding.", src=__name__)
padder = PKCS7(128).padder()
data = padder.update(data) + padder.finalize()

cipher = Cipher(self.alg, CBC(iv))
decryptor = cipher.decryptor()
padded_data = decryptor.update(data) + decryptor.finalize()
try:
padded_data = decryptor.update(data) + decryptor.finalize()
except ValueError as exception:
# Only raised in strict mode. Non-strict mode fixes padding.
raise PdfStreamError(exception)

unpadder = PKCS7(128).unpadder()
try:
Expand Down
11 changes: 10 additions & 1 deletion pypdf/_crypt_providers/_pycryptodome.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,17 @@ def decrypt(self, data: bytes, *, strict: bool = True) -> bytes:
if not data:
return data

if not strict and len(data) % 16 != 0:
logger_warning("Adding missing padding.", src=__name__)
data = pad(data, 16)

aes = AES.new(self.key, AES.MODE_CBC, iv)
padded_data = aes.decrypt(data)
try:
padded_data = aes.decrypt(data)
except ValueError as exception:
# Only raised in strict mode. Non-strict mode fixes padding.
raise PdfStreamError(exception)

try:
return unpad(padded_data, 16)
except ValueError as exception:
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ Source = "https://github.com/py-pdf/pypdf"
"Bug Reports" = "https://github.com/py-pdf/pypdf/issues"

[project.optional-dependencies]
crypto = ["cryptography"]
# This is due to the backend parameter being required back then:
# https://cryptography.io/en/latest/changelog/#v3-1
crypto = ["cryptography>3.0"]
cryptodome = ["PyCryptodome"]
fonts = ["fonttools"]
image = ["Pillow>=8.0.0"]
full = [
"cryptography",
"cryptography>3.0",
"fonttools",
"Pillow>=8.0.0"
]
Expand Down
16 changes: 16 additions & 0 deletions tests/test_encryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,22 @@ def test_aes_decrypt__wrong_padding(caplog):
)
caplog.clear()

with pytest.raises(
PdfStreamError,
match=(
r"^(The length of the provided data is not a multiple of the block length\.|"
r"Data must be padded to 16 byte boundary in CBC mode)$"
)
):
aes.decrypt(encrypted[:-2])

assert aes.decrypt(encrypted[:-2], strict=False) != original
assert caplog.messages[0] == "Adding missing padding."
assert re.match(
r"^Ignoring padding error: (Invalid padding bytes|(PKCS#7 p|P)adding is incorrect)\.$",
caplog.messages[1]
)


@pytest.mark.samples
def test_encrypt_stream_dictionary(pdf_file_path):
Expand Down
Loading