_encryption.py 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184
  1. # Copyright (c) 2022, exiledkingcc
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met:
  7. #
  8. # * Redistributions of source code must retain the above copyright notice,
  9. # this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above copyright notice,
  11. # this list of conditions and the following disclaimer in the documentation
  12. # and/or other materials provided with the distribution.
  13. # * The name of the author may not be used to endorse or promote products
  14. # derived from this software without specific prior written permission.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  20. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  23. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  24. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  25. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  26. # POSSIBILITY OF SUCH DAMAGE.
  27. import hashlib
  28. import secrets
  29. import struct
  30. from enum import Enum, IntEnum
  31. from typing import Any, Optional, Union, cast
  32. from pypdf._crypt_providers import (
  33. CryptAES,
  34. CryptBase,
  35. CryptIdentity,
  36. CryptRC4,
  37. aes_cbc_decrypt,
  38. aes_cbc_encrypt,
  39. aes_ecb_decrypt,
  40. aes_ecb_encrypt,
  41. rc4_decrypt,
  42. rc4_encrypt,
  43. )
  44. from ._utils import logger_warning
  45. from .generic import (
  46. ArrayObject,
  47. ByteStringObject,
  48. DictionaryObject,
  49. NameObject,
  50. NumberObject,
  51. PdfObject,
  52. StreamObject,
  53. TextStringObject,
  54. create_string_object,
  55. )
  56. class CryptFilter:
  57. def __init__(
  58. self,
  59. stm_crypt: CryptBase,
  60. str_crypt: CryptBase,
  61. ef_crypt: CryptBase,
  62. ) -> None:
  63. self.stm_crypt = stm_crypt
  64. self.str_crypt = str_crypt
  65. self.ef_crypt = ef_crypt
  66. def encrypt_object(self, obj: PdfObject) -> PdfObject:
  67. if isinstance(obj, ByteStringObject):
  68. data = self.str_crypt.encrypt(obj.original_bytes)
  69. obj = ByteStringObject(data)
  70. elif isinstance(obj, TextStringObject):
  71. data = self.str_crypt.encrypt(obj.get_encoded_bytes())
  72. obj = ByteStringObject(data)
  73. elif isinstance(obj, StreamObject):
  74. obj2 = StreamObject()
  75. obj2.update(obj)
  76. obj2.set_data(self.stm_crypt.encrypt(obj._data))
  77. for key, value in obj.items(): # Dont forget the Stream dict.
  78. obj2[key] = self.encrypt_object(value)
  79. obj = obj2
  80. elif isinstance(obj, DictionaryObject):
  81. obj2 = DictionaryObject() # type: ignore
  82. for key, value in obj.items():
  83. obj2[key] = self.encrypt_object(value)
  84. obj = obj2
  85. elif isinstance(obj, ArrayObject):
  86. obj = ArrayObject(self.encrypt_object(x) for x in obj)
  87. return obj
  88. def decrypt_object(self, obj: PdfObject) -> PdfObject:
  89. if isinstance(obj, (ByteStringObject, TextStringObject)):
  90. data = self.str_crypt.decrypt(obj.original_bytes)
  91. obj = create_string_object(data)
  92. elif isinstance(obj, StreamObject):
  93. obj._data = self.stm_crypt.decrypt(obj._data)
  94. for key, value in obj.items(): # Dont forget the Stream dict.
  95. obj[key] = self.decrypt_object(value)
  96. elif isinstance(obj, DictionaryObject):
  97. for key, value in obj.items():
  98. obj[key] = self.decrypt_object(value)
  99. elif isinstance(obj, ArrayObject):
  100. for i in range(len(obj)):
  101. obj[i] = self.decrypt_object(obj[i])
  102. return obj
  103. _PADDING = (
  104. b"\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
  105. b"\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"
  106. )
  107. def _padding(data: bytes) -> bytes:
  108. return (data + _PADDING)[:32]
  109. class AlgV4:
  110. @staticmethod
  111. def compute_key(
  112. password: bytes,
  113. rev: int,
  114. key_size: int,
  115. o_entry: bytes,
  116. P: int,
  117. id1_entry: bytes,
  118. metadata_encrypted: bool,
  119. ) -> bytes:
  120. """
  121. Algorithm 2: Computing an encryption key.
  122. a) Pad or truncate the password string to exactly 32 bytes. If the
  123. password string is more than 32 bytes long,
  124. use only its first 32 bytes; if it is less than 32 bytes long, pad it
  125. by appending the required number of
  126. additional bytes from the beginning of the following padding string:
  127. < 28 BF 4E 5E 4E 75 8A 41 64 00 4E 56 FF FA 01 08
  128. 2E 2E 00 B6 D0 68 3E 80 2F 0C A9 FE 64 53 69 7A >
  129. That is, if the password string is n bytes long, append
  130. the first 32 - n bytes of the padding string to the end
  131. of the password string. If the password string is empty
  132. (zero-length), meaning there is no user password,
  133. substitute the entire padding string in its place.
  134. b) Initialize the MD5 hash function and pass the result of step (a)
  135. as input to this function.
  136. c) Pass the value of the encryption dictionary’s O entry to the
  137. MD5 hash function. ("Algorithm 3: Computing
  138. the encryption dictionary’s O (owner password) value" shows how the
  139. O value is computed.)
  140. d) Convert the integer value of the P entry to a 32-bit unsigned binary
  141. number and pass these bytes to the
  142. MD5 hash function, low-order byte first.
  143. e) Pass the first element of the file’s file identifier array (the value
  144. of the ID entry in the document’s trailer
  145. dictionary; see Table 15) to the MD5 hash function.
  146. f) (Security handlers of revision 4 or greater) If document metadata is
  147. not being encrypted, pass 4 bytes with
  148. the value 0xFFFFFFFF to the MD5 hash function.
  149. g) Finish the hash.
  150. h) (Security handlers of revision 3 or greater) Do the following
  151. 50 times: Take the output from the previous
  152. MD5 hash and pass the first n bytes of the output as input into a new
  153. MD5 hash, where n is the number of
  154. bytes of the encryption key as defined by the value of the encryption
  155. dictionary’s Length entry.
  156. i) Set the encryption key to the first n bytes of the output from the
  157. final MD5 hash, where n shall always be 5
  158. for security handlers of revision 2 but, for security handlers of
  159. revision 3 or greater, shall depend on the
  160. value of the encryption dictionary’s Length entry.
  161. Args:
  162. password: The encryption secret as a bytes-string
  163. rev: The encryption revision (see PDF standard)
  164. key_size: The size of the key in bytes
  165. o_entry: The owner entry
  166. P: A set of flags specifying which operations shall be permitted
  167. when the document is opened with user access. If bit 2 is set to 1,
  168. all other bits are ignored and all operations are permitted.
  169. If bit 2 is set to 0, permission for operations are based on the
  170. values of the remaining flags defined in Table 24.
  171. id1_entry:
  172. metadata_encrypted: A boolean indicating if the metadata is encrypted.
  173. Returns:
  174. The u_hash digest of length key_size
  175. """
  176. a = _padding(password)
  177. u_hash = hashlib.md5(a)
  178. u_hash.update(o_entry)
  179. u_hash.update(struct.pack("<I", P))
  180. u_hash.update(id1_entry)
  181. if rev >= 4 and not metadata_encrypted:
  182. u_hash.update(b"\xff\xff\xff\xff")
  183. u_hash_digest = u_hash.digest()
  184. length = key_size // 8
  185. if rev >= 3:
  186. for _ in range(50):
  187. u_hash_digest = hashlib.md5(u_hash_digest[:length]).digest()
  188. return u_hash_digest[:length]
  189. @staticmethod
  190. def compute_O_value_key(owner_password: bytes, rev: int, key_size: int) -> bytes:
  191. """
  192. Algorithm 3: Computing the encryption dictionary’s O (owner password) value.
  193. a) Pad or truncate the owner password string as described in step (a)
  194. of "Algorithm 2: Computing an encryption key".
  195. If there is no owner password, use the user password instead.
  196. b) Initialize the MD5 hash function and pass the result of step (a) as
  197. input to this function.
  198. c) (Security handlers of revision 3 or greater) Do the following 50 times:
  199. Take the output from the previous
  200. MD5 hash and pass it as input into a new MD5 hash.
  201. d) Create an RC4 encryption key using the first n bytes of the output
  202. from the final MD5 hash, where n shall
  203. always be 5 for security handlers of revision 2 but, for security
  204. handlers of revision 3 or greater, shall
  205. depend on the value of the encryption dictionary’s Length entry.
  206. e) Pad or truncate the user password string as described in step (a) of
  207. "Algorithm 2: Computing an encryption key".
  208. f) Encrypt the result of step (e), using an RC4 encryption function with
  209. the encryption key obtained in step (d).
  210. g) (Security handlers of revision 3 or greater) Do the following 19 times:
  211. Take the output from the previous
  212. invocation of the RC4 function and pass it as input to a new
  213. invocation of the function; use an encryption
  214. key generated by taking each byte of the encryption key obtained in
  215. step (d) and performing an XOR
  216. (exclusive or) operation between that byte and the single-byte value
  217. of the iteration counter (from 1 to 19).
  218. h) Store the output from the final invocation of the RC4 function as
  219. the value of the O entry in the encryption dictionary.
  220. Args:
  221. owner_password:
  222. rev: The encryption revision (see PDF standard)
  223. key_size: The size of the key in bytes
  224. Returns:
  225. The RC4 key
  226. """
  227. a = _padding(owner_password)
  228. o_hash_digest = hashlib.md5(a).digest()
  229. if rev >= 3:
  230. for _ in range(50):
  231. o_hash_digest = hashlib.md5(o_hash_digest).digest()
  232. return o_hash_digest[: key_size // 8]
  233. @staticmethod
  234. def compute_O_value(rc4_key: bytes, user_password: bytes, rev: int) -> bytes:
  235. """
  236. See :func:`compute_O_value_key`.
  237. Args:
  238. rc4_key:
  239. user_password:
  240. rev: The encryption revision (see PDF standard)
  241. Returns:
  242. The RC4 encrypted
  243. """
  244. a = _padding(user_password)
  245. rc4_enc = rc4_encrypt(rc4_key, a)
  246. if rev >= 3:
  247. for i in range(1, 20):
  248. key = bytes(x ^ i for x in rc4_key)
  249. rc4_enc = rc4_encrypt(key, rc4_enc)
  250. return rc4_enc
  251. @staticmethod
  252. def compute_U_value(key: bytes, rev: int, id1_entry: bytes) -> bytes:
  253. """
  254. Algorithm 4: Computing the encryption dictionary’s U (user password) value.
  255. (Security handlers of revision 2)
  256. a) Create an encryption key based on the user password string, as
  257. described in "Algorithm 2: Computing an encryption key".
  258. b) Encrypt the 32-byte padding string shown in step (a) of
  259. "Algorithm 2: Computing an encryption key", using an RC4 encryption
  260. function with the encryption key from the preceding step.
  261. c) Store the result of step (b) as the value of the U entry in the
  262. encryption dictionary.
  263. Args:
  264. key:
  265. rev: The encryption revision (see PDF standard)
  266. id1_entry:
  267. Returns:
  268. The value
  269. """
  270. if rev <= 2:
  271. return rc4_encrypt(key, _PADDING)
  272. """
  273. Algorithm 5: Computing the encryption dictionary’s U (user password) value.
  274. (Security handlers of revision 3 or greater)
  275. a) Create an encryption key based on the user password string, as
  276. described in "Algorithm 2: Computing an encryption key".
  277. b) Initialize the MD5 hash function and pass the 32-byte padding string
  278. shown in step (a) of "Algorithm 2:
  279. Computing an encryption key" as input to this function.
  280. c) Pass the first element of the file’s file identifier array (the value
  281. of the ID entry in the document’s trailer
  282. dictionary; see Table 15) to the hash function and finish the hash.
  283. d) Encrypt the 16-byte result of the hash, using an RC4 encryption
  284. function with the encryption key from step (a).
  285. e) Do the following 19 times: Take the output from the previous
  286. invocation of the RC4 function and pass it as input to a new
  287. invocation of the function; use an encryption key generated by
  288. taking each byte of the original encryption key obtained in
  289. step (a) and performing an XOR (exclusive or) operation between that
  290. byte and the single-byte value of the iteration counter (from 1 to 19).
  291. f) Append 16 bytes of arbitrary padding to the output from the final
  292. invocation of the RC4 function and store the 32-byte result as the
  293. value of the U entry in the encryption dictionary.
  294. """
  295. u_hash = hashlib.md5(_PADDING)
  296. u_hash.update(id1_entry)
  297. rc4_enc = rc4_encrypt(key, u_hash.digest())
  298. for i in range(1, 20):
  299. rc4_key = bytes(x ^ i for x in key)
  300. rc4_enc = rc4_encrypt(rc4_key, rc4_enc)
  301. return _padding(rc4_enc)
  302. @staticmethod
  303. def verify_user_password(
  304. user_password: bytes,
  305. rev: int,
  306. key_size: int,
  307. o_entry: bytes,
  308. u_entry: bytes,
  309. P: int,
  310. id1_entry: bytes,
  311. metadata_encrypted: bool,
  312. ) -> bytes:
  313. """
  314. Algorithm 6: Authenticating the user password.
  315. a) Perform all but the last step of "Algorithm 4: Computing the
  316. encryption dictionary’s U (user password) value (Security handlers of
  317. revision 2)" or "Algorithm 5: Computing the encryption dictionary’s U
  318. (user password) value (Security handlers of revision 3 or greater)"
  319. using the supplied password string.
  320. b) If the result of step (a) is equal to the value of the encryption
  321. dictionary’s U entry (comparing on the first 16 bytes in the case of
  322. security handlers of revision 3 or greater), the password supplied is
  323. the correct user password. The key obtained in step (a) (that is, in
  324. the first step of "Algorithm 4: Computing the encryption
  325. dictionary’s U (user password) value
  326. (Security handlers of revision 2)" or
  327. "Algorithm 5: Computing the encryption dictionary’s U (user password)
  328. value (Security handlers of revision 3 or greater)") shall be used
  329. to decrypt the document.
  330. Args:
  331. user_password: The user password as a bytes stream
  332. rev: The encryption revision (see PDF standard)
  333. key_size: The size of the key in bytes
  334. o_entry: The owner entry
  335. u_entry: The user entry
  336. P: A set of flags specifying which operations shall be permitted
  337. when the document is opened with user access. If bit 2 is set to 1,
  338. all other bits are ignored and all operations are permitted.
  339. If bit 2 is set to 0, permission for operations are based on the
  340. values of the remaining flags defined in Table 24.
  341. id1_entry:
  342. metadata_encrypted: A boolean indicating if the metadata is encrypted.
  343. Returns:
  344. The key
  345. """
  346. key = AlgV4.compute_key(
  347. user_password, rev, key_size, o_entry, P, id1_entry, metadata_encrypted
  348. )
  349. u_value = AlgV4.compute_U_value(key, rev, id1_entry)
  350. if rev >= 3:
  351. u_value = u_value[:16]
  352. u_entry = u_entry[:16]
  353. if u_value != u_entry:
  354. key = b""
  355. return key
  356. @staticmethod
  357. def verify_owner_password(
  358. owner_password: bytes,
  359. rev: int,
  360. key_size: int,
  361. o_entry: bytes,
  362. u_entry: bytes,
  363. P: int,
  364. id1_entry: bytes,
  365. metadata_encrypted: bool,
  366. ) -> bytes:
  367. """
  368. Algorithm 7: Authenticating the owner password.
  369. a) Compute an encryption key from the supplied password string, as
  370. described in steps (a) to (d) of
  371. "Algorithm 3: Computing the encryption dictionary’s O (owner password)
  372. value".
  373. b) (Security handlers of revision 2 only) Decrypt the value of the
  374. encryption dictionary’s O entry, using an RC4
  375. encryption function with the encryption key computed in step (a).
  376. (Security handlers of revision 3 or greater) Do the following 20 times:
  377. Decrypt the value of the encryption dictionary’s O entry (first iteration)
  378. or the output from the previous iteration (all subsequent iterations),
  379. using an RC4 encryption function with a different encryption key at
  380. each iteration. The key shall be generated by taking the original key
  381. (obtained in step (a)) and performing an XOR (exclusive or) operation
  382. between each byte of the key and the single-byte value of the
  383. iteration counter (from 19 to 0).
  384. c) The result of step (b) purports to be the user password.
  385. Authenticate this user password using
  386. "Algorithm 6: Authenticating the user password".
  387. If it is correct, the password supplied is the correct owner password.
  388. Args:
  389. owner_password:
  390. rev: The encryption revision (see PDF standard)
  391. key_size: The size of the key in bytes
  392. o_entry: The owner entry
  393. u_entry: The user entry
  394. P: A set of flags specifying which operations shall be permitted
  395. when the document is opened with user access. If bit 2 is set to 1,
  396. all other bits are ignored and all operations are permitted.
  397. If bit 2 is set to 0, permission for operations are based on the
  398. values of the remaining flags defined in Table 24.
  399. id1_entry:
  400. metadata_encrypted: A boolean indicating if the metadata is encrypted.
  401. Returns:
  402. bytes
  403. """
  404. rc4_key = AlgV4.compute_O_value_key(owner_password, rev, key_size)
  405. if rev <= 2:
  406. user_password = rc4_decrypt(rc4_key, o_entry)
  407. else:
  408. user_password = o_entry
  409. for i in range(19, -1, -1):
  410. key = bytes(x ^ i for x in rc4_key)
  411. user_password = rc4_decrypt(key, user_password)
  412. return AlgV4.verify_user_password(
  413. user_password,
  414. rev,
  415. key_size,
  416. o_entry,
  417. u_entry,
  418. P,
  419. id1_entry,
  420. metadata_encrypted,
  421. )
  422. class AlgV5:
  423. @staticmethod
  424. def verify_owner_password(
  425. R: int, password: bytes, o_value: bytes, oe_value: bytes, u_value: bytes
  426. ) -> bytes:
  427. """
  428. Algorithm 3.2a Computing an encryption key.
  429. To understand the algorithm below, it is necessary to treat the O and U
  430. strings in the Encrypt dictionary as made up of three sections.
  431. The first 32 bytes are a hash value (explained below). The next 8 bytes
  432. are called the Validation Salt. The final 8 bytes are called the Key Salt.
  433. 1. The password string is generated from Unicode input by processing the
  434. input string with the SASLprep (IETF RFC 4013) profile of
  435. stringprep (IETF RFC 3454), and then converting to a UTF-8
  436. representation.
  437. 2. Truncate the UTF-8 representation to 127 bytes if it is longer than
  438. 127 bytes.
  439. 3. Test the password against the owner key by computing the SHA-256 hash
  440. of the UTF-8 password concatenated with the 8 bytes of owner
  441. Validation Salt, concatenated with the 48-byte U string. If the
  442. 32-byte result matches the first 32 bytes of the O string, this is
  443. the owner password.
  444. Compute an intermediate owner key by computing the SHA-256 hash of
  445. the UTF-8 password concatenated with the 8 bytes of owner Key Salt,
  446. concatenated with the 48-byte U string. The 32-byte result is the
  447. key used to decrypt the 32-byte OE string using AES-256 in CBC mode
  448. with no padding and an initialization vector of zero.
  449. The 32-byte result is the file encryption key.
  450. 4. Test the password against the user key by computing the SHA-256 hash
  451. of the UTF-8 password concatenated with the 8 bytes of user
  452. Validation Salt. If the 32 byte result matches the first 32 bytes of
  453. the U string, this is the user password.
  454. Compute an intermediate user key by computing the SHA-256 hash of the
  455. UTF-8 password concatenated with the 8 bytes of user Key Salt.
  456. The 32-byte result is the key used to decrypt the 32-byte
  457. UE string using AES-256 in CBC mode with no padding and an
  458. initialization vector of zero. The 32-byte result is the file
  459. encryption key.
  460. 5. Decrypt the 16-byte Perms string using AES-256 in ECB mode with an
  461. initialization vector of zero and the file encryption key as the key.
  462. Verify that bytes 9-11 of the result are the characters ‘a’, ‘d’, ‘b’.
  463. Bytes 0-3 of the decrypted Perms entry, treated as a little-endian
  464. integer, are the user permissions.
  465. They should match the value in the P key.
  466. Args:
  467. R: A number specifying which revision of the standard security
  468. handler shall be used to interpret this dictionary
  469. password: The owner password
  470. o_value: A 32-byte string, based on both the owner and user passwords,
  471. that shall be used in computing the encryption key and in
  472. determining whether a valid owner password was entered
  473. oe_value:
  474. u_value: A 32-byte string, based on the user password, that shall be
  475. used in determining whether to prompt the user for a password and,
  476. if so, whether a valid user or owner password was entered.
  477. Returns:
  478. The key
  479. """
  480. password = password[:127]
  481. if (
  482. AlgV5.calculate_hash(R, password, o_value[32:40], u_value[:48])
  483. != o_value[:32]
  484. ):
  485. return b""
  486. iv = bytes(0 for _ in range(16))
  487. tmp_key = AlgV5.calculate_hash(R, password, o_value[40:48], u_value[:48])
  488. return aes_cbc_decrypt(tmp_key, iv, oe_value)
  489. @staticmethod
  490. def verify_user_password(
  491. R: int, password: bytes, u_value: bytes, ue_value: bytes
  492. ) -> bytes:
  493. """
  494. See :func:`verify_owner_password`.
  495. Args:
  496. R: A number specifying which revision of the standard security
  497. handler shall be used to interpret this dictionary
  498. password: The user password
  499. u_value: A 32-byte string, based on the user password, that shall be
  500. used in determining whether to prompt the user for a password
  501. and, if so, whether a valid user or owner password was entered.
  502. ue_value:
  503. Returns:
  504. bytes
  505. """
  506. password = password[:127]
  507. if AlgV5.calculate_hash(R, password, u_value[32:40], b"") != u_value[:32]:
  508. return b""
  509. iv = bytes(0 for _ in range(16))
  510. tmp_key = AlgV5.calculate_hash(R, password, u_value[40:48], b"")
  511. return aes_cbc_decrypt(tmp_key, iv, ue_value)
  512. @staticmethod
  513. def calculate_hash(R: int, password: bytes, salt: bytes, udata: bytes) -> bytes:
  514. # https://github.com/qpdf/qpdf/blob/main/libqpdf/QPDF_encryption.cc
  515. k = hashlib.sha256(password + salt + udata).digest()
  516. if R < 6:
  517. return k
  518. count = 0
  519. while True:
  520. count += 1
  521. k1 = password + k + udata
  522. e = aes_cbc_encrypt(k[:16], k[16:32], k1 * 64)
  523. hash_fn = (
  524. hashlib.sha256,
  525. hashlib.sha384,
  526. hashlib.sha512,
  527. )[sum(e[:16]) % 3]
  528. k = hash_fn(e).digest()
  529. if count >= 64 and e[-1] <= count - 32:
  530. break
  531. return k[:32]
  532. @staticmethod
  533. def verify_perms(
  534. key: bytes, perms: bytes, p: int, metadata_encrypted: bool
  535. ) -> bool:
  536. """
  537. See :func:`verify_owner_password` and :func:`compute_perms_value`.
  538. Args:
  539. key: The owner password
  540. perms:
  541. p: A set of flags specifying which operations shall be permitted
  542. when the document is opened with user access.
  543. If bit 2 is set to 1, all other bits are ignored and all
  544. operations are permitted.
  545. If bit 2 is set to 0, permission for operations are based on
  546. the values of the remaining flags defined in Table 24.
  547. metadata_encrypted:
  548. Returns:
  549. A boolean
  550. """
  551. b8 = b"T" if metadata_encrypted else b"F"
  552. p1 = struct.pack("<I", p) + b"\xff\xff\xff\xff" + b8 + b"adb"
  553. p2 = aes_ecb_decrypt(key, perms)
  554. return p1 == p2[:12]
  555. @staticmethod
  556. def generate_values(
  557. R: int,
  558. user_password: bytes,
  559. owner_password: bytes,
  560. key: bytes,
  561. p: int,
  562. metadata_encrypted: bool,
  563. ) -> dict[Any, Any]:
  564. user_password = user_password[:127]
  565. owner_password = owner_password[:127]
  566. u_value, ue_value = AlgV5.compute_U_value(R, user_password, key)
  567. o_value, oe_value = AlgV5.compute_O_value(R, owner_password, key, u_value)
  568. perms = AlgV5.compute_Perms_value(key, p, metadata_encrypted)
  569. return {
  570. "/U": u_value,
  571. "/UE": ue_value,
  572. "/O": o_value,
  573. "/OE": oe_value,
  574. "/Perms": perms,
  575. }
  576. @staticmethod
  577. def compute_U_value(R: int, password: bytes, key: bytes) -> tuple[bytes, bytes]:
  578. """
  579. Algorithm 3.8 Computing the encryption dictionary’s U (user password)
  580. and UE (user encryption key) values.
  581. 1. Generate 16 random bytes of data using a strong random number generator.
  582. The first 8 bytes are the User Validation Salt. The second 8 bytes
  583. are the User Key Salt. Compute the 32-byte SHA-256 hash of the
  584. password concatenated with the User Validation Salt. The 48-byte
  585. string consisting of the 32-byte hash followed by the User
  586. Validation Salt followed by the User Key Salt is stored as the U key.
  587. 2. Compute the 32-byte SHA-256 hash of the password concatenated with
  588. the User Key Salt. Using this hash as the key, encrypt the file
  589. encryption key using AES-256 in CBC mode with no padding and an
  590. initialization vector of zero. The resulting 32-byte string is stored
  591. as the UE key.
  592. Args:
  593. R:
  594. password:
  595. key:
  596. Returns:
  597. A tuple (u-value, ue value)
  598. """
  599. random_bytes = secrets.token_bytes(16)
  600. val_salt = random_bytes[:8]
  601. key_salt = random_bytes[8:]
  602. u_value = AlgV5.calculate_hash(R, password, val_salt, b"") + val_salt + key_salt
  603. tmp_key = AlgV5.calculate_hash(R, password, key_salt, b"")
  604. iv = bytes(0 for _ in range(16))
  605. ue_value = aes_cbc_encrypt(tmp_key, iv, key)
  606. return u_value, ue_value
  607. @staticmethod
  608. def compute_O_value(
  609. R: int, password: bytes, key: bytes, u_value: bytes
  610. ) -> tuple[bytes, bytes]:
  611. """
  612. Algorithm 3.9 Computing the encryption dictionary’s O (owner password)
  613. and OE (owner encryption key) values.
  614. 1. Generate 16 random bytes of data using a strong random number
  615. generator. The first 8 bytes are the Owner Validation Salt. The
  616. second 8 bytes are the Owner Key Salt. Compute the 32-byte SHA-256
  617. hash of the password concatenated with the Owner Validation Salt and
  618. then concatenated with the 48-byte U string as generated in
  619. Algorithm 3.8. The 48-byte string consisting of the 32-byte hash
  620. followed by the Owner Validation Salt followed by the Owner Key Salt
  621. is stored as the O key.
  622. 2. Compute the 32-byte SHA-256 hash of the password concatenated with
  623. the Owner Key Salt and then concatenated with the 48-byte U string as
  624. generated in Algorithm 3.8. Using this hash as the key,
  625. encrypt the file encryption key using AES-256 in CBC mode with
  626. no padding and an initialization vector of zero.
  627. The resulting 32-byte string is stored as the OE key.
  628. Args:
  629. R:
  630. password:
  631. key:
  632. u_value: A 32-byte string, based on the user password, that shall be
  633. used in determining whether to prompt the user for a password
  634. and, if so, whether a valid user or owner password was entered.
  635. Returns:
  636. A tuple (O value, OE value)
  637. """
  638. random_bytes = secrets.token_bytes(16)
  639. val_salt = random_bytes[:8]
  640. key_salt = random_bytes[8:]
  641. o_value = (
  642. AlgV5.calculate_hash(R, password, val_salt, u_value) + val_salt + key_salt
  643. )
  644. tmp_key = AlgV5.calculate_hash(R, password, key_salt, u_value[:48])
  645. iv = bytes(0 for _ in range(16))
  646. oe_value = aes_cbc_encrypt(tmp_key, iv, key)
  647. return o_value, oe_value
  648. @staticmethod
  649. def compute_Perms_value(key: bytes, p: int, metadata_encrypted: bool) -> bytes:
  650. """
  651. Algorithm 3.10 Computing the encryption dictionary’s Perms
  652. (permissions) value.
  653. 1. Extend the permissions (contents of the P integer) to 64 bits by
  654. setting the upper 32 bits to all 1’s.
  655. (This allows for future extension without changing the format.)
  656. 2. Record the 8 bytes of permission in the bytes 0-7 of the block,
  657. low order byte first.
  658. 3. Set byte 8 to the ASCII value ' T ' or ' F ' according to the
  659. EncryptMetadata Boolean.
  660. 4. Set bytes 9-11 to the ASCII characters ' a ', ' d ', ' b '.
  661. 5. Set bytes 12-15 to 4 bytes of random data, which will be ignored.
  662. 6. Encrypt the 16-byte block using AES-256 in ECB mode with an
  663. initialization vector of zero, using the file encryption key as the
  664. key. The result (16 bytes) is stored as the Perms string, and checked
  665. for validity when the file is opened.
  666. Args:
  667. key:
  668. p: A set of flags specifying which operations shall be permitted
  669. when the document is opened with user access. If bit 2 is set to 1,
  670. all other bits are ignored and all operations are permitted.
  671. If bit 2 is set to 0, permission for operations are based on the
  672. values of the remaining flags defined in Table 24.
  673. metadata_encrypted: A boolean indicating if the metadata is encrypted.
  674. Returns:
  675. The perms value
  676. """
  677. b8 = b"T" if metadata_encrypted else b"F"
  678. rr = secrets.token_bytes(4)
  679. data = struct.pack("<I", p) + b"\xff\xff\xff\xff" + b8 + b"adb" + rr
  680. return aes_ecb_encrypt(key, data)
  681. class PasswordType(IntEnum):
  682. NOT_DECRYPTED = 0
  683. USER_PASSWORD = 1
  684. OWNER_PASSWORD = 2
  685. class EncryptAlgorithm(tuple, Enum): # type: ignore # noqa: SLOT001
  686. # V, R, Length
  687. RC4_40 = (1, 2, 40)
  688. RC4_128 = (2, 3, 128)
  689. AES_128 = (4, 4, 128)
  690. AES_256_R5 = (5, 5, 256)
  691. AES_256 = (5, 6, 256)
  692. class EncryptionValues:
  693. O: bytes # noqa: E741
  694. U: bytes
  695. OE: bytes
  696. UE: bytes
  697. Perms: bytes
  698. class Encryption:
  699. """
  700. Collects and manages parameters for PDF document encryption and decryption.
  701. Args:
  702. V: A code specifying the algorithm to be used in encrypting and
  703. decrypting the document.
  704. R: The revision of the standard security handler.
  705. Length: The length of the encryption key in bits.
  706. P: A set of flags specifying which operations shall be permitted
  707. when the document is opened with user access
  708. entry: The encryption dictionary object.
  709. EncryptMetadata: Whether to encrypt metadata in the document.
  710. first_id_entry: The first 16 bytes of the file's original ID.
  711. StmF: The name of the crypt filter that shall be used by default
  712. when decrypting streams.
  713. StrF: The name of the crypt filter that shall be used when decrypting
  714. all strings in the document.
  715. EFF: The name of the crypt filter that shall be used when
  716. encrypting embedded file streams that do not have their own
  717. crypt filter specifier.
  718. values: Additional encryption parameters.
  719. """
  720. def __init__(
  721. self,
  722. V: int,
  723. R: int,
  724. Length: int,
  725. P: int,
  726. entry: DictionaryObject,
  727. EncryptMetadata: bool,
  728. first_id_entry: bytes,
  729. StmF: str,
  730. StrF: str,
  731. EFF: str,
  732. values: Optional[EncryptionValues],
  733. ) -> None:
  734. # §7.6.2, entries common to all encryption dictionaries
  735. # use same name as keys of encryption dictionaries entries
  736. self.V = V
  737. self.R = R
  738. self.Length = Length # key_size
  739. self.P = (P + 0x100000000) % 0x100000000 # maybe P < 0
  740. self.EncryptMetadata = EncryptMetadata
  741. self.id1_entry = first_id_entry
  742. self.StmF = StmF
  743. self.StrF = StrF
  744. self.EFF = EFF
  745. self.values: EncryptionValues = values or EncryptionValues()
  746. self._password_type = PasswordType.NOT_DECRYPTED
  747. self._key: Optional[bytes] = None
  748. self._are_permissions_valid: bool = True
  749. def is_decrypted(self) -> bool:
  750. return self._password_type != PasswordType.NOT_DECRYPTED
  751. def encrypt_object(self, obj: PdfObject, idnum: int, generation: int) -> PdfObject:
  752. # skip calculate key
  753. if not self._is_encryption_object(obj):
  754. return obj
  755. cf = self._make_crypt_filter(idnum, generation)
  756. return cf.encrypt_object(obj)
  757. def decrypt_object(self, obj: PdfObject, idnum: int, generation: int) -> PdfObject:
  758. # skip calculate key
  759. if not self._is_encryption_object(obj):
  760. return obj
  761. cf = self._make_crypt_filter(idnum, generation)
  762. return cf.decrypt_object(obj)
  763. @staticmethod
  764. def _is_encryption_object(obj: PdfObject) -> bool:
  765. return isinstance(
  766. obj,
  767. (
  768. ByteStringObject,
  769. TextStringObject,
  770. StreamObject,
  771. ArrayObject,
  772. DictionaryObject,
  773. ),
  774. )
  775. def _make_crypt_filter(self, idnum: int, generation: int) -> CryptFilter:
  776. """
  777. Algorithm 1: Encryption of data using the RC4 or AES algorithms.
  778. a) Obtain the object number and generation number from the object
  779. identifier of the string or stream to be encrypted
  780. (see 7.3.10, "Indirect Objects"). If the string is a direct object,
  781. use the identifier of the indirect object containing it.
  782. b) For all strings and streams without crypt filter specifier; treating
  783. the object number and generation number as binary integers, extend
  784. the original n-byte encryption key to n + 5 bytes by appending the
  785. low-order 3 bytes of the object number and the low-order 2 bytes of
  786. the generation number in that order, low-order byte first.
  787. (n is 5 unless the value of V in the encryption dictionary is greater
  788. than 1, in which case n is the value of Length divided by 8.)
  789. If using the AES algorithm, extend the encryption key an additional
  790. 4 bytes by adding the value “sAlT”, which corresponds to the
  791. hexadecimal values 0x73, 0x41, 0x6C, 0x54. (This addition is done for
  792. backward compatibility and is not intended to provide additional
  793. security.)
  794. c) Initialize the MD5 hash function and pass the result of step (b) as
  795. input to this function.
  796. d) Use the first (n + 5) bytes, up to a maximum of 16, of the output
  797. from the MD5 hash as the key for the RC4 or AES symmetric key
  798. algorithms, along with the string or stream data to be encrypted.
  799. If using the AES algorithm, the Cipher Block Chaining (CBC) mode,
  800. which requires an initialization vector, is used. The block size
  801. parameter is set to 16 bytes, and the initialization vector is a
  802. 16-byte random number that is stored as the first 16 bytes of the
  803. encrypted stream or string.
  804. Algorithm 3.1a Encryption of data using the AES algorithm
  805. 1. Use the 32-byte file encryption key for the AES-256 symmetric key
  806. algorithm, along with the string or stream data to be encrypted.
  807. Use the AES algorithm in Cipher Block Chaining (CBC) mode, which
  808. requires an initialization vector. The block size parameter is set to
  809. 16 bytes, and the initialization vector is a 16-byte random number
  810. that is stored as the first 16 bytes of the encrypted stream or string.
  811. The output is the encrypted data to be stored in the PDF file.
  812. """
  813. pack1 = struct.pack("<i", idnum)[:3]
  814. pack2 = struct.pack("<i", generation)[:2]
  815. assert self._key
  816. key = self._key
  817. n = 5 if self.V == 1 else self.Length // 8
  818. key_data = key[:n] + pack1 + pack2
  819. key_hash = hashlib.md5(key_data)
  820. rc4_key = key_hash.digest()[: min(n + 5, 16)]
  821. # for AES-128
  822. key_hash.update(b"sAlT")
  823. aes128_key = key_hash.digest()[: min(n + 5, 16)]
  824. # for AES-256
  825. aes256_key = key
  826. stm_crypt = self._get_crypt(self.StmF, rc4_key, aes128_key, aes256_key)
  827. str_crypt = self._get_crypt(self.StrF, rc4_key, aes128_key, aes256_key)
  828. ef_crypt = self._get_crypt(self.EFF, rc4_key, aes128_key, aes256_key)
  829. return CryptFilter(stm_crypt, str_crypt, ef_crypt)
  830. @staticmethod
  831. def _get_crypt(
  832. method: str, rc4_key: bytes, aes128_key: bytes, aes256_key: bytes
  833. ) -> CryptBase:
  834. if method == "/AESV2":
  835. return CryptAES(aes128_key)
  836. if method == "/AESV3":
  837. return CryptAES(aes256_key)
  838. if method == "/Identity":
  839. return CryptIdentity()
  840. return CryptRC4(rc4_key)
  841. @staticmethod
  842. def _encode_password(password: Union[bytes, str]) -> bytes:
  843. if isinstance(password, str):
  844. try:
  845. pwd = password.encode("latin-1")
  846. except Exception:
  847. pwd = password.encode("utf-8")
  848. else:
  849. pwd = password
  850. return pwd
  851. def verify(self, password: Union[bytes, str]) -> PasswordType:
  852. pwd = self._encode_password(password)
  853. key, rc = self.verify_v4(pwd) if self.V <= 4 else self.verify_v5(pwd)
  854. if rc != PasswordType.NOT_DECRYPTED:
  855. self._password_type = rc
  856. self._key = key
  857. return rc
  858. def verify_v4(self, password: bytes) -> tuple[bytes, PasswordType]:
  859. # verify owner password first
  860. key = AlgV4.verify_owner_password(
  861. password,
  862. self.R,
  863. self.Length,
  864. self.values.O,
  865. self.values.U,
  866. self.P,
  867. self.id1_entry,
  868. self.EncryptMetadata,
  869. )
  870. if key:
  871. return key, PasswordType.OWNER_PASSWORD
  872. key = AlgV4.verify_user_password(
  873. password,
  874. self.R,
  875. self.Length,
  876. self.values.O,
  877. self.values.U,
  878. self.P,
  879. self.id1_entry,
  880. self.EncryptMetadata,
  881. )
  882. if key:
  883. return key, PasswordType.USER_PASSWORD
  884. return b"", PasswordType.NOT_DECRYPTED
  885. def verify_v5(self, password: bytes) -> tuple[bytes, PasswordType]:
  886. # TODO: use SASLprep process
  887. # verify owner password first
  888. key = AlgV5.verify_owner_password(
  889. self.R, password, self.values.O, self.values.OE, self.values.U
  890. )
  891. rc = PasswordType.OWNER_PASSWORD
  892. if not key:
  893. key = AlgV5.verify_user_password(
  894. self.R, password, self.values.U, self.values.UE
  895. )
  896. rc = PasswordType.USER_PASSWORD
  897. if not key:
  898. return b"", PasswordType.NOT_DECRYPTED
  899. # verify Perms
  900. self._are_permissions_valid = AlgV5.verify_perms(key, self.values.Perms, self.P, self.EncryptMetadata)
  901. if not self._are_permissions_valid:
  902. logger_warning("ignore '/Perms' verify failed", __name__)
  903. return key, rc
  904. def write_entry(
  905. self, user_password: str, owner_password: Optional[str]
  906. ) -> DictionaryObject:
  907. user_pwd = self._encode_password(user_password)
  908. owner_pwd = self._encode_password(owner_password) if owner_password else None
  909. if owner_pwd is None:
  910. owner_pwd = user_pwd
  911. if self.V <= 4:
  912. self.compute_values_v4(user_pwd, owner_pwd)
  913. else:
  914. self._key = secrets.token_bytes(self.Length // 8)
  915. values = AlgV5.generate_values(
  916. self.R, user_pwd, owner_pwd, self._key, self.P, self.EncryptMetadata
  917. )
  918. self.values.O = values["/O"]
  919. self.values.U = values["/U"]
  920. self.values.OE = values["/OE"]
  921. self.values.UE = values["/UE"]
  922. self.values.Perms = values["/Perms"]
  923. dict_obj = DictionaryObject()
  924. dict_obj[NameObject("/V")] = NumberObject(self.V)
  925. dict_obj[NameObject("/R")] = NumberObject(self.R)
  926. dict_obj[NameObject("/Length")] = NumberObject(self.Length)
  927. dict_obj[NameObject("/P")] = NumberObject(self.P)
  928. dict_obj[NameObject("/Filter")] = NameObject("/Standard")
  929. # ignore /EncryptMetadata
  930. dict_obj[NameObject("/O")] = ByteStringObject(self.values.O)
  931. dict_obj[NameObject("/U")] = ByteStringObject(self.values.U)
  932. if self.V >= 4:
  933. # TODO: allow different method
  934. std_cf = DictionaryObject()
  935. std_cf[NameObject("/AuthEvent")] = NameObject("/DocOpen")
  936. std_cf[NameObject("/CFM")] = NameObject(self.StmF)
  937. std_cf[NameObject("/Length")] = NumberObject(self.Length // 8)
  938. cf = DictionaryObject()
  939. cf[NameObject("/StdCF")] = std_cf
  940. dict_obj[NameObject("/CF")] = cf
  941. dict_obj[NameObject("/StmF")] = NameObject("/StdCF")
  942. dict_obj[NameObject("/StrF")] = NameObject("/StdCF")
  943. # ignore EFF
  944. # dict_obj[NameObject("/EFF")] = NameObject("/StdCF")
  945. if self.V >= 5:
  946. dict_obj[NameObject("/OE")] = ByteStringObject(self.values.OE)
  947. dict_obj[NameObject("/UE")] = ByteStringObject(self.values.UE)
  948. dict_obj[NameObject("/Perms")] = ByteStringObject(self.values.Perms)
  949. return dict_obj
  950. def compute_values_v4(self, user_password: bytes, owner_password: bytes) -> None:
  951. rc4_key = AlgV4.compute_O_value_key(owner_password, self.R, self.Length)
  952. o_value = AlgV4.compute_O_value(rc4_key, user_password, self.R)
  953. key = AlgV4.compute_key(
  954. user_password,
  955. self.R,
  956. self.Length,
  957. o_value,
  958. self.P,
  959. self.id1_entry,
  960. self.EncryptMetadata,
  961. )
  962. u_value = AlgV4.compute_U_value(key, self.R, self.id1_entry)
  963. self._key = key
  964. self.values.O = o_value
  965. self.values.U = u_value
  966. @staticmethod
  967. def read(encryption_entry: DictionaryObject, first_id_entry: bytes) -> "Encryption":
  968. if encryption_entry.get("/Filter") != "/Standard":
  969. raise NotImplementedError(
  970. "only Standard PDF encryption handler is available"
  971. )
  972. if "/SubFilter" in encryption_entry:
  973. raise NotImplementedError("/SubFilter NOT supported")
  974. stm_filter = "/V2"
  975. str_filter = "/V2"
  976. ef_filter = "/V2"
  977. alg_ver = encryption_entry.get("/V", 0)
  978. if alg_ver not in (1, 2, 3, 4, 5):
  979. raise NotImplementedError(f"Encryption V={alg_ver} NOT supported")
  980. if alg_ver >= 4:
  981. filters = encryption_entry["/CF"]
  982. stm_filter = encryption_entry.get("/StmF", "/Identity")
  983. str_filter = encryption_entry.get("/StrF", "/Identity")
  984. ef_filter = encryption_entry.get("/EFF", stm_filter)
  985. if stm_filter != "/Identity":
  986. stm_filter = filters[stm_filter]["/CFM"] # type: ignore
  987. if str_filter != "/Identity":
  988. str_filter = filters[str_filter]["/CFM"] # type: ignore
  989. if ef_filter != "/Identity":
  990. ef_filter = filters[ef_filter]["/CFM"] # type: ignore
  991. allowed_methods = ("/Identity", "/V2", "/AESV2", "/AESV3")
  992. if stm_filter not in allowed_methods:
  993. raise NotImplementedError(f"StmF Method {stm_filter} NOT supported!")
  994. if str_filter not in allowed_methods:
  995. raise NotImplementedError(f"StrF Method {str_filter} NOT supported!")
  996. if ef_filter not in allowed_methods:
  997. raise NotImplementedError(f"EFF Method {ef_filter} NOT supported!")
  998. alg_rev = cast(int, encryption_entry["/R"])
  999. perm_flags = cast(int, encryption_entry["/P"])
  1000. key_bits = encryption_entry.get("/Length", 40)
  1001. if alg_ver == 4 and stm_filter == "/AESV2":
  1002. cf_dict = cast(DictionaryObject, filters[encryption_entry["/StmF"]]) # type: ignore[index]
  1003. # CF /Length is in bytes (default 16 for AES-128), convert to bits
  1004. key_bits = cast(int, cf_dict.get("/Length", 16)) * 8
  1005. encrypt_metadata = encryption_entry.get("/EncryptMetadata")
  1006. encrypt_metadata = (
  1007. encrypt_metadata.value if encrypt_metadata is not None else True
  1008. )
  1009. values = EncryptionValues()
  1010. values.O = cast(ByteStringObject, encryption_entry["/O"]).original_bytes
  1011. values.U = cast(ByteStringObject, encryption_entry["/U"]).original_bytes
  1012. values.OE = encryption_entry.get("/OE", ByteStringObject()).original_bytes
  1013. values.UE = encryption_entry.get("/UE", ByteStringObject()).original_bytes
  1014. values.Perms = encryption_entry.get("/Perms", ByteStringObject()).original_bytes
  1015. return Encryption(
  1016. V=alg_ver,
  1017. R=alg_rev,
  1018. Length=key_bits,
  1019. P=perm_flags,
  1020. EncryptMetadata=encrypt_metadata,
  1021. first_id_entry=first_id_entry,
  1022. values=values,
  1023. StrF=str_filter,
  1024. StmF=stm_filter,
  1025. EFF=ef_filter,
  1026. entry=encryption_entry, # Dummy entry for the moment; will get removed
  1027. )
  1028. @staticmethod
  1029. def make(
  1030. alg: EncryptAlgorithm, permissions: int, first_id_entry: bytes
  1031. ) -> "Encryption":
  1032. alg_ver, alg_rev, key_bits = alg
  1033. stm_filter, str_filter, ef_filter = "/V2", "/V2", "/V2"
  1034. if alg == EncryptAlgorithm.AES_128:
  1035. stm_filter, str_filter, ef_filter = "/AESV2", "/AESV2", "/AESV2"
  1036. elif alg in (EncryptAlgorithm.AES_256_R5, EncryptAlgorithm.AES_256):
  1037. stm_filter, str_filter, ef_filter = "/AESV3", "/AESV3", "/AESV3"
  1038. return Encryption(
  1039. V=alg_ver,
  1040. R=alg_rev,
  1041. Length=key_bits,
  1042. P=permissions,
  1043. EncryptMetadata=True,
  1044. first_id_entry=first_id_entry,
  1045. values=None,
  1046. StrF=str_filter,
  1047. StmF=stm_filter,
  1048. EFF=ef_filter,
  1049. entry=DictionaryObject(), # Dummy entry for the moment; will get removed
  1050. )