import pytest import pikepdf @pytest.fixture def trivial(resources): return pikepdf.open(resources / 'pal-1bit-trivial.pdf') @pytest.fixture def graph_encrypted(resources): return pikepdf.open(resources / 'graph-encrypted.pdf', password='owner') @pytest.mark.parametrize( "R,owner,user", [ (6, "foo", "bar"), (4, "password", "password"), (3, "12345678", "secret"), (2, "qwerty", "123456"), ], ) def test_encrypt_basic(trivial, outpdf, R, owner, user): trivial.save(outpdf, encryption=dict(R=R, owner=owner, user=user)) pdf_owner = pikepdf.open(outpdf, password=owner) assert pdf_owner.is_encrypted pdf_user = pikepdf.open(outpdf, password=user) assert pdf_user.is_encrypted def test_encrypt_R5(trivial, outpdf): with pytest.warns(UserWarning): trivial.save(outpdf, encryption=dict(R=5, owner='foo', user='foo')) @pytest.mark.parametrize("R", [-1, 0, 1, 7, 9, 42]) def test_encrypt_invalid_level_value(trivial, outpdf, R): with pytest.raises(ValueError): trivial.save(outpdf, encryption=dict(R=R, owner='foo', user='foo')) @pytest.mark.parametrize("R", [3.14, '6', b'6', None]) def test_encrypt_invalid_level(trivial, outpdf, R): with pytest.raises(TypeError): trivial.save(outpdf, encryption=dict(R=R, owner='foo', user='foo')) def test_encrypt_without_owner(trivial, outpdf): trivial.save(outpdf, encryption=dict(user='foo')) def test_encrypt_no_passwords(trivial, outpdf): trivial.save(outpdf, encryption=dict(R=6)) def test_encrypt_permissions_deny(trivial, outpdf): perms = pikepdf.models.Permissions(extract=False) trivial.save( outpdf, encryption=pikepdf.Encryption(owner='sun', user='moon', allow=perms) ) pdf = pikepdf.open(outpdf, password='sun') assert not pdf.allow.extract assert pdf.allow.modify_form def test_encrypt_info(trivial, outpdf): trivial.save(outpdf, encryption=dict(R=4, owner='foo', user='bar')) pdf = pikepdf.open(outpdf, password='foo') assert pdf.encryption.user_password == b'bar' assert pdf.encryption.bits == 128 @pytest.mark.parametrize( "R,owner,user,aes,metadata,err", [ (6, "foo", "bar", 42, False, r"aes.*bool"), (6, "password", "password", True, 42, r"metadata.*bool"), (3, "12345678", "secret", False, True, r"metadata.*R < 4"), (2, "qwerty", "123456", True, False, r"AES.*R < 4"), (6, "rc4", "rc4", False, True, r"R = 6.*AES"), (4, "met", "met", False, True, r"unless AES"), ], ) def test_bad_settings(trivial, outpdf, R, owner, user, aes, metadata, err): with pytest.raises(Exception, match=err): trivial.save( outpdf, encryption=pikepdf.Encryption( R=R, owner=owner, user=user, aes=aes, metadata=metadata ), ) def test_block_encryption_and_normalize(trivial, outpdf): with pytest.raises(ValueError, match=r'encryption and normalize_content'): trivial.save( outpdf, encryption=pikepdf.Encryption(owner='foo', user='bar'), normalize_content=True, ) def test_consistency_saving_removes_encryption(graph_encrypted, outpdf): # This was not intended behavior. It's a side effect of unconditionally calling # w.setDecodeLevel(), which disables preserving encryption in # QPDFWriter::doWriteSetup() graph_encrypted.save(outpdf) with pikepdf.open(outpdf) as pdf: assert not pdf.is_encrypted def test_save_without_encryption(graph_encrypted, outpdf): graph_encrypted.save(outpdf, encryption=False) with pikepdf.open(outpdf) as pdf: assert not pdf.is_encrypted def test_save_preserve_encryption(graph_encrypted, outpdf): graph_encrypted.save(outpdf, encryption=True) with pikepdf.open(outpdf, 'owner') as pdf: assert pdf.is_encrypted def test_preserve_encryption_not_encrypted(trivial, outpdf): with pytest.raises(ValueError): trivial.save(outpdf, encryption=True)