Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pikepdf/models/encryption.py: 81%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

69 statements  

1# SPDX-FileCopyrightText: 2022 James R. Barlow 

2# SPDX-License-Identifier: MPL-2.0 

3 

4"""For managing PDF encryption.""" 

5 

6from __future__ import annotations 

7 

8from typing import TYPE_CHECKING, Any, Literal, NamedTuple, cast 

9 

10if TYPE_CHECKING: 

11 from pikepdf._core import EncryptionMethod 

12 

13 

14class Permissions(NamedTuple): 

15 """Stores the user-level permissions for an encrypted PDF. 

16 

17 A compliant PDF reader/writer should enforce these restrictions on people 

18 who have the user password and not the owner password. In practice, either 

19 password is sufficient to decrypt all document contents. A person who has 

20 the owner password should be allowed to modify the document in any way. 

21 pikepdf does not enforce the restrictions in any way; it is up to application 

22 developers to enforce them as they see fit. 

23 

24 Unencrypted PDFs implicitly have all permissions allowed. Permissions can 

25 only be changed when a PDF is saved. 

26 """ 

27 

28 accessibility: bool = True 

29 """Deprecated in PDF 2.0. Formerly used to block accessibility tools. 

30 

31 In older versions of the PDF specification, it was possible to request 

32 a PDF reader to block a user's right to use accessibility tools. Modern 

33 PDF readers do not support this archaic feature and always allow accessibility 

34 tools to be used. The only purpose of this permission is to provide 

35 testing of this deprecated feature. 

36 """ 

37 

38 extract: bool = True 

39 """Can users extract contents?""" 

40 

41 modify_annotation: bool = True 

42 """Can users modify annotations?""" 

43 

44 modify_assembly: bool = False 

45 """Can users arrange document contents?""" 

46 

47 modify_form: bool = True 

48 """Can users fill out forms?""" 

49 

50 modify_other: bool = True 

51 """Can users modify the document?""" 

52 

53 print_lowres: bool = True 

54 """Can users print the document at low resolution?""" 

55 

56 print_highres: bool = True 

57 """Can users print the document at high resolution?""" 

58 

59 

60DEFAULT_PERMISSIONS = Permissions() 

61 

62 

63class EncryptionInfo: 

64 """Reports encryption information for an encrypted PDF. 

65 

66 This information may not be changed, except when a PDF is saved. 

67 This object is not used to specify the encryption settings to save 

68 a PDF, due to non-overlapping information requirements. 

69 """ 

70 

71 def __init__(self, encdict: dict[str, Any]): 

72 """Initialize EncryptionInfo. 

73 

74 Generally pikepdf will initialize and return it. 

75 

76 Args: 

77 encdict: Python dictionary containing encryption settings. 

78 """ 

79 self._encdict = encdict 

80 

81 @property 

82 def R(self) -> int: 

83 """Revision number of the security handler.""" 

84 return int(self._encdict['R']) 

85 

86 @property 

87 def V(self) -> int: 

88 """Version of PDF password algorithm.""" 

89 return int(self._encdict['V']) 

90 

91 @property 

92 def P(self) -> int: 

93 """Return encoded permission bits. 

94 

95 See :meth:`Pdf.allow` instead. 

96 """ 

97 return int(self._encdict['P']) 

98 

99 @property 

100 def stream_method(self) -> EncryptionMethod: 

101 """Encryption method used to encode streams.""" 

102 return cast('EncryptionMethod', self._encdict['stream']) 

103 

104 @property 

105 def string_method(self) -> EncryptionMethod: 

106 """Encryption method used to encode strings.""" 

107 return cast('EncryptionMethod', self._encdict['string']) 

108 

109 @property 

110 def file_method(self) -> EncryptionMethod: 

111 """Encryption method used to encode the whole file.""" 

112 return cast('EncryptionMethod', self._encdict['file']) 

113 

114 @property 

115 def user_password(self) -> bytes: 

116 """If possible, return the user password. 

117 

118 The user password can only be retrieved when a PDF is opened 

119 with the owner password and when older versions of the 

120 encryption algorithm are used. 

121 

122 The password is always returned as ``bytes`` even if it has 

123 a clear Unicode representation. 

124 """ 

125 return bytes(self._encdict['user_passwd']) 

126 

127 @property 

128 def encryption_key(self) -> bytes: 

129 """Return the RC4 or AES encryption key used for this file.""" 

130 return bytes(self._encdict['encryption_key']) 

131 

132 @property 

133 def bits(self) -> int: 

134 """Return the number of bits in the encryption algorithm. 

135 

136 e.g. if the algorithm is AES-256, this returns 256. 

137 """ 

138 return len(self._encdict['encryption_key']) * 8 

139 

140 def __repr__(self): 

141 return ( 

142 f'<{self.__class__.__name__}: {self.R=}, {self.V=}, {self.P=} ' 

143 f'{self.stream_method=}, {self.string_method=}, ' 

144 f'{self.file_method=}, {self.user_password=}, ' 

145 f'{self.encryption_key=}, {self.bits=}>' 

146 ) 

147 

148 

149class Encryption(NamedTuple): 

150 """Specify the encryption settings to apply when a PDF is saved.""" 

151 

152 owner: str = '' 

153 """The owner password to use. This allows full control 

154 of the file. If blank, the PDF will be encrypted and 

155 present as "(SECURED)" in PDF viewers. If the owner password 

156 is blank, the user password should be as well.""" 

157 

158 user: str = '' 

159 """The user password to use. With this password, some 

160 restrictions will be imposed by a typical PDF reader. 

161 If blank, the PDF can be opened by anyone, but only modified 

162 as allowed by the permissions in ``allow``.""" 

163 

164 R: Literal[2, 3, 4, 5, 6] = 6 

165 """Select the security handler algorithm to use. Choose from: 

166 ``2``, ``3``, ``4`` or ``6``. By default, the highest version of 

167 is selected (``6``). ``5`` is a deprecated algorithm that should 

168 not be used.""" 

169 

170 allow: Permissions = DEFAULT_PERMISSIONS 

171 """The permissions to set. 

172 If omitted, all permissions are granted to the user.""" 

173 

174 aes: bool = True 

175 """If True, request the AES algorithm. If False, use RC4. 

176 If omitted, AES is selected whenever possible (R >= 4).""" 

177 

178 metadata: bool = True 

179 """If True, also encrypt the PDF metadata. If False, 

180 metadata is not encrypted. Reading document metadata without 

181 decryption may be desirable in some cases. Requires ``aes=True``. 

182 If omitted, metadata is encrypted whenever possible."""