diff --git a/docs/api-docs/pyhanko.pdf_utils.crypt.rst b/docs/api-docs/pyhanko.pdf_utils.crypt.rst index 09d392b7..69404cba 100644 --- a/docs/api-docs/pyhanko.pdf_utils.crypt.rst +++ b/docs/api-docs/pyhanko.pdf_utils.crypt.rst @@ -29,6 +29,14 @@ pyhanko.pdf\_utils.crypt.filter\_mixins module :undoc-members: :show-inheritance: +pyhanko.pdf\_utils.crypt.permissions module +----------------------------------- + +.. automodule:: pyhanko.pdf_utils.crypt.permissions + :members: + :undoc-members: + :show-inheritance: + pyhanko.pdf\_utils.crypt.pubkey module -------------------------------------- diff --git a/pyhanko/pdf_utils/crypt/permissions.py b/pyhanko/pdf_utils/crypt/permissions.py index f2f1f919..9f93fc46 100644 --- a/pyhanko/pdf_utils/crypt/permissions.py +++ b/pyhanko/pdf_utils/crypt/permissions.py @@ -3,15 +3,33 @@ from enum import Flag from functools import reduce +__all__ = ['PdfPermissions', 'StandardPermissions', 'PubKeyPermissions'] + class PdfPermissions(Flag): + """ + Utility mixin for PDF permission flags. + """ + + # We purposefully do not inherit from IntFlag since + # PDF uses 32-bit twos complement to treat flags as ints, + # which doesn't jive well with what IntFlag would do, + # so it's hard to detect backwards compatibility issues. @classmethod def allow_everything(cls): + """ + Set all permissions. + """ + return reduce(operator.or_, cls.__members__.values()) @classmethod def from_uint(cls, uint_flags: int): + """ + Convert a 32-bit unsigned integer into PDF permission flags. + """ + result = cls(0) for flag in cls: if uint_flags & flag.value: @@ -20,28 +38,54 @@ def from_uint(cls, uint_flags: int): @classmethod def from_bytes(cls, flags: bytes): + """ + Convert a string of 4 bytes into PDF permission flags. + """ + uint_flags = struct.unpack('>I', flags)[0] return cls.from_uint(uint_flags) @classmethod def from_sint32(cls, sint32_flags: int): + """ + Convert a 32-bit signed integer into PDF permission flags. + """ + return cls.from_uint(sint32_flags & 0xFFFFFFFF) - def as_uint32(self): + def as_uint32(self) -> int: + """ + Convert a set of PDF permission flags to their 32-bit + unsigned integer representation. + + This will already take into account some conventions + in the PDF specification, i.e. to set as-yet undefined + permission flags to 'Allow'. + """ + raise NotImplementedError def as_bytes(self) -> bytes: + """ + Convert a set of PDF permission flags to their binary + representation. + """ return struct.pack('>I', self.as_uint32()) def as_sint32(self) -> int: + """ + Convert a set of PDF permission flags to their + signed integer representation. + """ return struct.unpack('>i', self.as_bytes())[0] class StandardPermissions(PdfPermissions, Flag): - # We purposefully do not inherit from IntFlag since - # PDF uses 32-bit twos complement to treat flags as ints, - # which doesn't jive well with what IntFlag would do, - # so it's hard to detect backwards compatibility issues. + """ + Permission flags for the standard security handler. + + See Table 22 in ISO 32000-2:2020. + """ ALLOW_PRINTING = 4 ALLOW_MODIFICATION_GENERIC = 8 @@ -52,11 +96,17 @@ class StandardPermissions(PdfPermissions, Flag): ALLOW_REASSEMBLY = 1024 ALLOW_HIGH_QUALITY_PRINTING = 2048 - def as_uint32(self): + def as_uint32(self) -> int: return sum(x.value for x in self.__class__ if x in self) | 0xFFFFF0C0 class PubKeyPermissions(PdfPermissions, Flag): + """ + Permission flags for the public-key security handler. + + See Table 24 in ISO 32000-2:2020. + """ + ALLOW_ENCRYPTION_CHANGE = 2 ALLOW_PRINTING = 4 ALLOW_MODIFICATION_GENERIC = 8 @@ -67,6 +117,6 @@ class PubKeyPermissions(PdfPermissions, Flag): ALLOW_REASSEMBLY = 1024 ALLOW_HIGH_QUALITY_PRINTING = 2048 - def as_uint32(self): + def as_uint32(self) -> int: # ensure the first bit is set for compatibility with Acrobat return sum(x.value for x in self.__class__ if x in self) | 0xFFFFF0C1