@awacloud/pdf 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CHANGELOG.md +609 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +77 -0
  4. package/README.md +363 -2
  5. package/dist/build/index.js +21 -0
  6. package/dist/build/pdf-full-rw.js +10972 -0
  7. package/dist/build/pdf-full-rw.meta.json +105 -0
  8. package/dist/build/pdf-full-rw.min.js +53 -0
  9. package/dist/build/pdf-full.js +6078 -0
  10. package/dist/build/pdf-full.meta.json +90 -0
  11. package/dist/build/pdf-full.min.js +32 -0
  12. package/dist/build/pdf-large-rw.js +10367 -0
  13. package/dist/build/pdf-large-rw.meta.json +99 -0
  14. package/dist/build/pdf-large-rw.min.js +53 -0
  15. package/dist/build/pdf-large.js +5473 -0
  16. package/dist/build/pdf-large.meta.json +84 -0
  17. package/dist/build/pdf-large.min.js +32 -0
  18. package/dist/build/pdf-legacy-rw.js +12402 -0
  19. package/dist/build/pdf-legacy-rw.meta.json +110 -0
  20. package/dist/build/pdf-legacy-rw.min.js +53 -0
  21. package/dist/build/pdf-legacy.js +7508 -0
  22. package/dist/build/pdf-legacy.meta.json +95 -0
  23. package/dist/build/pdf-legacy.min.js +32 -0
  24. package/dist/build/pdf-rw.js +7578 -0
  25. package/dist/build/pdf-rw.meta.json +77 -0
  26. package/dist/build/pdf-rw.min.js +53 -0
  27. package/dist/build/pdf.js +2684 -0
  28. package/dist/build/pdf.meta.json +62 -0
  29. package/dist/build/pdf.min.js +32 -0
  30. package/dist/standalone/pdf-full-rw.js +16798 -0
  31. package/dist/standalone/pdf-full-rw.meta.json +78 -0
  32. package/dist/standalone/pdf-full-rw.min.js +56 -0
  33. package/dist/standalone/pdf-full.js +11904 -0
  34. package/dist/standalone/pdf-full.meta.json +63 -0
  35. package/dist/standalone/pdf-full.min.js +35 -0
  36. package/dist/standalone/pdf-large-rw.js +16193 -0
  37. package/dist/standalone/pdf-large-rw.meta.json +72 -0
  38. package/dist/standalone/pdf-large-rw.min.js +56 -0
  39. package/dist/standalone/pdf-large.js +11299 -0
  40. package/dist/standalone/pdf-large.meta.json +57 -0
  41. package/dist/standalone/pdf-large.min.js +35 -0
  42. package/dist/standalone/pdf-legacy-rw.js +18228 -0
  43. package/dist/standalone/pdf-legacy-rw.meta.json +83 -0
  44. package/dist/standalone/pdf-legacy-rw.min.js +56 -0
  45. package/dist/standalone/pdf-legacy.js +13334 -0
  46. package/dist/standalone/pdf-legacy.meta.json +68 -0
  47. package/dist/standalone/pdf-legacy.min.js +35 -0
  48. package/dist/standalone/pdf-rw.js +13404 -0
  49. package/dist/standalone/pdf-rw.meta.json +50 -0
  50. package/dist/standalone/pdf-rw.min.js +56 -0
  51. package/dist/standalone/pdf.js +8510 -0
  52. package/dist/standalone/pdf.meta.json +35 -0
  53. package/dist/standalone/pdf.min.js +35 -0
  54. package/docs/README.md +53 -0
  55. package/docs/api/README.md +38 -0
  56. package/docs/api/_shared/README.md +91 -0
  57. package/docs/api/action/README.md +29 -0
  58. package/docs/api/action/action.md +81 -0
  59. package/docs/api/action/goTo.md +66 -0
  60. package/docs/api/action/launch.md +58 -0
  61. package/docs/api/action/named.md +55 -0
  62. package/docs/api/action/uri.md +54 -0
  63. package/docs/api/annot/README.md +53 -0
  64. package/docs/api/annot/annot.md +114 -0
  65. package/docs/api/annot/fileAttach.md +53 -0
  66. package/docs/api/annot/freeText.md +68 -0
  67. package/docs/api/annot/ink.md +69 -0
  68. package/docs/api/annot/link.md +74 -0
  69. package/docs/api/annot/markup.md +83 -0
  70. package/docs/api/annot/popup.md +52 -0
  71. package/docs/api/annot/projection.md +56 -0
  72. package/docs/api/annot/redact.md +67 -0
  73. package/docs/api/annot/square.md +87 -0
  74. package/docs/api/annot/stamp.md +54 -0
  75. package/docs/api/annot/text.md +69 -0
  76. package/docs/api/annot/widget.md +69 -0
  77. package/docs/api/associatedFiles/README.md +9 -0
  78. package/docs/api/associatedFiles/associatedFiles.md +78 -0
  79. package/docs/api/bundles/README.md +68 -0
  80. package/docs/api/bundles/dist-matrix.md +165 -0
  81. package/docs/api/bundles/pdf-full.md +148 -0
  82. package/docs/api/bundles/pdf-large.md +144 -0
  83. package/docs/api/bundles/pdf-legacy.md +169 -0
  84. package/docs/api/content/README.md +29 -0
  85. package/docs/api/content/color.md +99 -0
  86. package/docs/api/content/graphics.md +114 -0
  87. package/docs/api/content/images.md +124 -0
  88. package/docs/api/content/ops.md +100 -0
  89. package/docs/api/content/stream.md +107 -0
  90. package/docs/api/content/text.md +98 -0
  91. package/docs/api/crypto/README.md +29 -0
  92. package/docs/api/crypto/aesGcm.md +72 -0
  93. package/docs/api/crypto/permissions.md +79 -0
  94. package/docs/api/crypto/security.md +98 -0
  95. package/docs/api/crypto/standardV4.md +104 -0
  96. package/docs/api/crypto/standardV5.md +84 -0
  97. package/docs/api/crypto/standardV6.md +93 -0
  98. package/docs/api/destination/README.md +9 -0
  99. package/docs/api/destination/destination.md +79 -0
  100. package/docs/api/document/README.md +29 -0
  101. package/docs/api/document/builder.md +281 -0
  102. package/docs/api/document/catalog.md +98 -0
  103. package/docs/api/document/document.md +187 -0
  104. package/docs/api/document/encryptedWriter.md +149 -0
  105. package/docs/api/document/incrementalWriter.md +148 -0
  106. package/docs/api/document/page.md +99 -0
  107. package/docs/api/document/pages.md +82 -0
  108. package/docs/api/document/resources.md +102 -0
  109. package/docs/api/document/writer.md +157 -0
  110. package/docs/api/document/xrefStreamWriter.md +122 -0
  111. package/docs/api/embedded/README.md +13 -0
  112. package/docs/api/embedded/collection.md +80 -0
  113. package/docs/api/embedded/embeddedFile.md +86 -0
  114. package/docs/api/embedded/fileSpec.md +87 -0
  115. package/docs/api/errors.md +110 -0
  116. package/docs/api/extra/3d-richmedia.md +76 -0
  117. package/docs/api/extra/README.md +99 -0
  118. package/docs/api/extra/annot-extended.md +71 -0
  119. package/docs/api/extra/associated-files.md +70 -0
  120. package/docs/api/extra/ccitt-fax-decoder.md +74 -0
  121. package/docs/api/extra/color-spaces-extended.md +72 -0
  122. package/docs/api/extra/content-ops-extended.md +82 -0
  123. package/docs/api/extra/document-parts.md +69 -0
  124. package/docs/api/extra/embedded-files-portfolio.md +87 -0
  125. package/docs/api/extra/font-cid-typed.md +77 -0
  126. package/docs/api/extra/font-color-tagging.md +76 -0
  127. package/docs/api/extra/form-actions-extended.md +75 -0
  128. package/docs/api/extra/info-dict-deprecated.md +72 -0
  129. package/docs/api/extra/jbig2-read.md +80 -0
  130. package/docs/api/extra/legacy-deprecated-annots.md +89 -0
  131. package/docs/api/extra/legacy-deprecated-filters.md +78 -0
  132. package/docs/api/extra/legacy-rc4-read.md +74 -0
  133. package/docs/api/extra/legacy-xfa-read.md +65 -0
  134. package/docs/api/extra/linearization-write.md +71 -0
  135. package/docs/api/extra/misc.md +93 -0
  136. package/docs/api/extra/optional-content-extended.md +83 -0
  137. package/docs/api/extra/pdf-a-output-intent.md +65 -0
  138. package/docs/api/extra/pdf-sandbox.md +76 -0
  139. package/docs/api/extra/pdf-ua-tagged.md +63 -0
  140. package/docs/api/extra/pdf-x-prepress.md +65 -0
  141. package/docs/api/extra/redaction-iso32005.md +65 -0
  142. package/docs/api/extra/shading-typed.md +73 -0
  143. package/docs/api/extra/sig-aes-gcm.md +69 -0
  144. package/docs/api/extra/sig-pades.md +103 -0
  145. package/docs/api/extra/tagged-pdf-typed.md +78 -0
  146. package/docs/api/extra/transparency-typed.md +74 -0
  147. package/docs/api/extra/well-tagged-pdf.md +61 -0
  148. package/docs/api/extra/xmp-extended.md +65 -0
  149. package/docs/api/font/README.md +25 -0
  150. package/docs/api/font/embed.md +157 -0
  151. package/docs/api/font/encoding.md +95 -0
  152. package/docs/api/font/font.md +97 -0
  153. package/docs/api/font/type3.md +89 -0
  154. package/docs/api/form/README.md +35 -0
  155. package/docs/api/form/acroform.md +88 -0
  156. package/docs/api/form/appearance.md +87 -0
  157. package/docs/api/form/button.md +97 -0
  158. package/docs/api/form/choice.md +96 -0
  159. package/docs/api/form/fieldTree.md +93 -0
  160. package/docs/api/form/signature.md +90 -0
  161. package/docs/api/form/text.md +88 -0
  162. package/docs/api/linearization/README.md +11 -0
  163. package/docs/api/linearization/linearization.md +81 -0
  164. package/docs/api/main.md +116 -0
  165. package/docs/api/metadata/README.md +10 -0
  166. package/docs/api/metadata/info.md +70 -0
  167. package/docs/api/metadata/xmp.md +62 -0
  168. package/docs/api/ocg/README.md +23 -0
  169. package/docs/api/ocg/config.md +95 -0
  170. package/docs/api/ocg/ocg.md +77 -0
  171. package/docs/api/outline/README.md +11 -0
  172. package/docs/api/outline/outline.md +107 -0
  173. package/docs/api/pdf.md +152 -0
  174. package/docs/api/prepress/README.md +10 -0
  175. package/docs/api/prepress/outputIntent.md +79 -0
  176. package/docs/api/prepress/pageBoundary.md +75 -0
  177. package/docs/api/sig/README.md +32 -0
  178. package/docs/api/sig/byteRange.md +120 -0
  179. package/docs/api/sig/certChain.md +84 -0
  180. package/docs/api/sig/dss.md +111 -0
  181. package/docs/api/sig/oids.md +76 -0
  182. package/docs/api/sig/sha1.md +72 -0
  183. package/docs/api/sig/sign.md +317 -0
  184. package/docs/api/sig/signature.md +178 -0
  185. package/docs/api/sig/timestamp.md +84 -0
  186. package/docs/api/syntax/README.md +29 -0
  187. package/docs/api/syntax/crossRefStream.md +115 -0
  188. package/docs/api/syntax/filters/README.md +50 -0
  189. package/docs/api/syntax/filters/ascii85.md +76 -0
  190. package/docs/api/syntax/filters/asciiHex.md +73 -0
  191. package/docs/api/syntax/filters/dispatch.md +125 -0
  192. package/docs/api/syntax/filters/flate.md +134 -0
  193. package/docs/api/syntax/filters/runLength.md +78 -0
  194. package/docs/api/syntax/objStream.md +88 -0
  195. package/docs/api/syntax/parser-obj.md +97 -0
  196. package/docs/api/syntax/parser.md +151 -0
  197. package/docs/api/syntax/serializer.md +109 -0
  198. package/docs/api/syntax/tokenizer.md +104 -0
  199. package/docs/api/syntax/trailer.md +85 -0
  200. package/docs/api/syntax/xref.md +139 -0
  201. package/docs/api/tagged/README.md +25 -0
  202. package/docs/api/tagged/classMap.md +67 -0
  203. package/docs/api/tagged/markedContent.md +62 -0
  204. package/docs/api/tagged/parentTree.md +67 -0
  205. package/docs/api/tagged/roleMap.md +67 -0
  206. package/docs/api/tagged/structElement.md +76 -0
  207. package/docs/api/tagged/structTree.md +75 -0
  208. package/docs/guide/coverage.md +113 -0
  209. package/docs/guide/crypto.md +121 -0
  210. package/docs/guide/extending.md +76 -0
  211. package/docs/guide/getting-started.md +75 -0
  212. package/docs/guide/legacy-1.7.md +42 -0
  213. package/docs/guide/pades-integration.md +579 -0
  214. package/docs/guide/read-pdf.md +89 -0
  215. package/package.json +97 -4
  216. package/src/_shared/index.js +179 -0
  217. package/src/action/action.js +119 -0
  218. package/src/action/goTo.js +89 -0
  219. package/src/action/launch.js +61 -0
  220. package/src/action/named.js +54 -0
  221. package/src/action/uri.js +51 -0
  222. package/src/annot/annot.js +212 -0
  223. package/src/annot/fileAttach.js +55 -0
  224. package/src/annot/freeText.js +82 -0
  225. package/src/annot/ink.js +77 -0
  226. package/src/annot/link.js +77 -0
  227. package/src/annot/markup.js +91 -0
  228. package/src/annot/popup.js +53 -0
  229. package/src/annot/projection.js +52 -0
  230. package/src/annot/redact.js +87 -0
  231. package/src/annot/square.js +132 -0
  232. package/src/annot/stamp.js +48 -0
  233. package/src/annot/text.js +54 -0
  234. package/src/annot/widget.js +61 -0
  235. package/src/associatedFiles/associatedFiles.js +86 -0
  236. package/src/bundles/pdf-full.js +91 -0
  237. package/src/bundles/pdf-large.js +81 -0
  238. package/src/bundles/pdf-legacy.js +107 -0
  239. package/src/content/color.js +114 -0
  240. package/src/content/graphics.js +192 -0
  241. package/src/content/images.js +160 -0
  242. package/src/content/ops.js +137 -0
  243. package/src/content/stream.js +154 -0
  244. package/src/content/text.js +125 -0
  245. package/src/crypto/aesGcm.js +123 -0
  246. package/src/crypto/permissions.js +112 -0
  247. package/src/crypto/security.js +327 -0
  248. package/src/crypto/standardV4.js +443 -0
  249. package/src/crypto/standardV5.js +306 -0
  250. package/src/crypto/standardV6.js +334 -0
  251. package/src/destination/destination.js +183 -0
  252. package/src/document/builder.js +618 -0
  253. package/src/document/catalog.js +100 -0
  254. package/src/document/document.js +472 -0
  255. package/src/document/encryptedWriter.js +554 -0
  256. package/src/document/incrementalWriter.js +514 -0
  257. package/src/document/page.js +131 -0
  258. package/src/document/pages.js +103 -0
  259. package/src/document/resources.js +146 -0
  260. package/src/document/writer.js +211 -0
  261. package/src/document/xrefStreamWriter.js +353 -0
  262. package/src/embedded/collection.js +102 -0
  263. package/src/embedded/embeddedFile.js +99 -0
  264. package/src/embedded/fileSpec.js +137 -0
  265. package/src/errors.js +78 -0
  266. package/src/extra/3d-richmedia.js +171 -0
  267. package/src/extra/annot-extended.js +200 -0
  268. package/src/extra/associated-files.js +131 -0
  269. package/src/extra/ccitt-fax-decoder.js +776 -0
  270. package/src/extra/color-spaces-extended.js +196 -0
  271. package/src/extra/content-ops-extended.js +153 -0
  272. package/src/extra/document-parts.js +149 -0
  273. package/src/extra/embedded-files-portfolio.js +234 -0
  274. package/src/extra/font-cid-typed.js +185 -0
  275. package/src/extra/font-color-tagging.js +144 -0
  276. package/src/extra/form-actions-extended.js +196 -0
  277. package/src/extra/info-dict-deprecated.js +137 -0
  278. package/src/extra/jbig2-read.js +169 -0
  279. package/src/extra/legacy-deprecated-annots.js +198 -0
  280. package/src/extra/legacy-deprecated-filters.js +167 -0
  281. package/src/extra/legacy-rc4-read.js +235 -0
  282. package/src/extra/legacy-xfa-read.js +104 -0
  283. package/src/extra/linearization-write.js +97 -0
  284. package/src/extra/misc.js +217 -0
  285. package/src/extra/optional-content-extended.js +142 -0
  286. package/src/extra/pdf-a-output-intent.js +112 -0
  287. package/src/extra/pdf-sandbox.js +88 -0
  288. package/src/extra/pdf-ua-tagged.js +116 -0
  289. package/src/extra/pdf-x-prepress.js +114 -0
  290. package/src/extra/redaction-iso32005.js +136 -0
  291. package/src/extra/shading-typed.js +222 -0
  292. package/src/extra/sig-aes-gcm.js +135 -0
  293. package/src/extra/sig-pades.js +242 -0
  294. package/src/extra/tagged-pdf-typed.js +203 -0
  295. package/src/extra/transparency-typed.js +135 -0
  296. package/src/extra/well-tagged-pdf.js +138 -0
  297. package/src/extra/xmp-extended.js +190 -0
  298. package/src/font/embed.js +480 -0
  299. package/src/font/encoding.js +92 -0
  300. package/src/font/font.js +101 -0
  301. package/src/font/type3.js +75 -0
  302. package/src/form/acroform.js +94 -0
  303. package/src/form/appearance.js +90 -0
  304. package/src/form/button.js +105 -0
  305. package/src/form/choice.js +152 -0
  306. package/src/form/fieldTree.js +120 -0
  307. package/src/form/signature.js +100 -0
  308. package/src/form/text.js +101 -0
  309. package/src/linearization/linearization.js +107 -0
  310. package/src/main.js +411 -0
  311. package/src/metadata/info.js +87 -0
  312. package/src/metadata/xmp.js +62 -0
  313. package/src/ocg/config.js +156 -0
  314. package/src/ocg/ocg.js +124 -0
  315. package/src/outline/outline.js +157 -0
  316. package/src/pdf.js +133 -0
  317. package/src/prepress/outputIntent.js +118 -0
  318. package/src/prepress/pageBoundary.js +108 -0
  319. package/src/sig/byteRange.js +306 -0
  320. package/src/sig/certChain.js +247 -0
  321. package/src/sig/dss.js +317 -0
  322. package/src/sig/oids.js +157 -0
  323. package/src/sig/sha1.js +142 -0
  324. package/src/sig/sign.js +1899 -0
  325. package/src/sig/signature.js +1441 -0
  326. package/src/sig/timestamp.js +236 -0
  327. package/src/syntax/crossRefStream.js +133 -0
  328. package/src/syntax/filters/ascii85.js +122 -0
  329. package/src/syntax/filters/asciiHex.js +83 -0
  330. package/src/syntax/filters/dispatch.js +176 -0
  331. package/src/syntax/filters/flate.js +316 -0
  332. package/src/syntax/filters/runLength.js +96 -0
  333. package/src/syntax/objStream.js +99 -0
  334. package/src/syntax/parser-obj.js +52 -0
  335. package/src/syntax/parser.js +321 -0
  336. package/src/syntax/serializer.js +221 -0
  337. package/src/syntax/tokenizer.js +290 -0
  338. package/src/syntax/trailer.js +76 -0
  339. package/src/syntax/xref.js +341 -0
  340. package/src/tagged/classMap.js +81 -0
  341. package/src/tagged/markedContent.js +123 -0
  342. package/src/tagged/parentTree.js +126 -0
  343. package/src/tagged/roleMap.js +107 -0
  344. package/src/tagged/structElement.js +138 -0
  345. package/src/tagged/structTree.js +94 -0
@@ -0,0 +1,443 @@
1
+ // Copyright (c) 2026 AwaCloud SAS
2
+ // Author: Matthieu Bouilloux
3
+ // SPDX-License-Identifier: AGPL-3.0-only
4
+ // Dual-licensed; see the NOTICE file for licensing and any additional terms.
5
+
6
+ /**
7
+ * @fileoverview Standard Security Handler v4 (V=4, R=4).
8
+ *
9
+ * Implements PDF 1.6 / ISO 32000-1 §7.6.3 Algorithm 2 (key derivation
10
+ * R=4, 128-bit), Algorithm 3 (/O), Algorithm 5 (/U), and the per-object
11
+ * key derivation for AESV2 (AES-128-CBC) and V2 (RC4-128) crypt filters.
12
+ *
13
+ * V=4 requires the /Encrypt dictionary to carry `/CF /StdCF` selecting
14
+ * either method:
15
+ *
16
+ * - AESV2 — AES-128-CBC; per-object key = MD5(fileKey || objId(3 LE)
17
+ * || gen(2 LE) || "sAlT") truncated to min(fileKey.len + 5, 16);
18
+ * IV prefixed, PKCS#7 padded.
19
+ * - V2 — RC4-128; per-object key = MD5(fileKey || objId(3 LE)
20
+ * || gen(2 LE)) truncated to min(fileKey.len + 5, 16).
21
+ *
22
+ * @module pdf/crypto/standardV4
23
+ */
24
+
25
+ /**
26
+ * Module factory — worker-safe, self-contained.
27
+ */
28
+ import { pdfErrors } from '../errors.js';
29
+ import { aes } from '@awacloud/fw/crypto/cipher/aes.js';
30
+ import { cbc } from '@awacloud/fw/crypto/mode/cbc.js';
31
+ import { bitArray } from '@awacloud/fw/crypto/utils/bitArray.js';
32
+
33
+ export const pdfStandardV4 = {
34
+ name: 'pdfStandardV4',
35
+ dependencies: ['pdfErrors', 'aes', 'cbc', 'bitArray'],
36
+ deps: [pdfErrors, aes, cbc, bitArray],
37
+ factory(errors, aes, cbc, bitArray) {
38
+ const { EncryptionError } = errors;
39
+
40
+ if (!aes || !cbc || !bitArray) {
41
+ throw new EncryptionError('pdf/crypto/v4/missing-fw',
42
+ 'pdfStandardV4 requires aes, cbc, bitArray fw modules');
43
+ }
44
+
45
+ const PASSWORD_PADDING = new Uint8Array([
46
+ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
47
+ 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
48
+ 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
49
+ 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
50
+ ]);
51
+ const AES_SALT = new Uint8Array([0x73, 0x41, 0x6C, 0x54]); // 'sAlT'
52
+
53
+ // ---- helpers ----
54
+
55
+ function bytesToWords(b, off, len) {
56
+ const n = len >>> 2;
57
+ const out = new Array(n);
58
+ for (let i = 0; i < n; i++) {
59
+ const j = off + (i << 2);
60
+ out[i] = ((b[j] << 24) | (b[j + 1] << 16) | (b[j + 2] << 8) | b[j + 3]) | 0;
61
+ }
62
+ return out;
63
+ }
64
+ function wordsToBytes(w, into, off) {
65
+ for (let i = 0; i < w.length; i++) {
66
+ const j = off + (i << 2);
67
+ into[j] = (w[i] >>> 24) & 0xff;
68
+ into[j + 1] = (w[i] >>> 16) & 0xff;
69
+ into[j + 2] = (w[i] >>> 8) & 0xff;
70
+ into[j + 3] = w[i] & 0xff;
71
+ }
72
+ }
73
+
74
+ // RC4 stream cipher (used for V2 method + Algorithm 5 /U).
75
+ function rc4(key, data) {
76
+ const S = new Uint8Array(256);
77
+ for (let i = 0; i < 256; i++) S[i] = i;
78
+ let j = 0;
79
+ for (let i = 0; i < 256; i++) {
80
+ j = (j + S[i] + key[i % key.length]) & 0xFF;
81
+ const t = S[i]; S[i] = S[j]; S[j] = t;
82
+ }
83
+ const out = new Uint8Array(data.length);
84
+ let a = 0, b = 0;
85
+ for (let n = 0; n < data.length; n++) {
86
+ a = (a + 1) & 0xFF;
87
+ b = (b + S[a]) & 0xFF;
88
+ const t = S[a]; S[a] = S[b]; S[b] = t;
89
+ const k = S[(S[a] + S[b]) & 0xFF];
90
+ out[n] = data[n] ^ k;
91
+ }
92
+ return out;
93
+ }
94
+
95
+ // MD5 — RFC 1321 reference, compacted.
96
+ function md5(bytes) {
97
+ function rol(x, n) { return ((x << n) | (x >>> (32 - n))) | 0; }
98
+ const r = [
99
+ 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
100
+ 5, 9,14,20,5, 9,14,20,5, 9,14,20,5, 9,14,20,
101
+ 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
102
+ 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
103
+ ];
104
+ const k = [
105
+ 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
106
+ 0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
107
+ 0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
108
+ 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
109
+ 0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
110
+ 0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
111
+ 0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
112
+ 0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
113
+ ];
114
+ const msgLenBytes = bytes.length;
115
+ const withOne = new Uint8Array(((msgLenBytes + 8) >> 6 << 6) + 64);
116
+ withOne.set(bytes);
117
+ withOne[msgLenBytes] = 0x80;
118
+ const bitsLo = (msgLenBytes * 8) >>> 0;
119
+ const bitsHi = Math.floor((msgLenBytes * 8) / 0x100000000) >>> 0;
120
+ const dv = new DataView(withOne.buffer);
121
+ dv.setUint32(withOne.length - 8, bitsLo, true);
122
+ dv.setUint32(withOne.length - 4, bitsHi, true);
123
+ let a0 = 0x67452301 | 0, b0 = 0xefcdab89 | 0, c0 = 0x98badcfe | 0, d0 = 0x10325476 | 0;
124
+ for (let off = 0; off < withOne.length; off += 64) {
125
+ const M = new Array(16);
126
+ for (let i = 0; i < 16; i++) M[i] = dv.getUint32(off + i * 4, true);
127
+ let A = a0, B = b0, C = c0, D = d0;
128
+ for (let i = 0; i < 64; i++) {
129
+ let F, g;
130
+ if (i < 16) { F = (B & C) | ((~B) & D); g = i; }
131
+ else if (i < 32) { F = (D & B) | ((~D) & C); g = (5 * i + 1) % 16; }
132
+ else if (i < 48) { F = B ^ C ^ D; g = (3 * i + 5) % 16; }
133
+ else { F = C ^ (B | (~D)); g = (7 * i) % 16; }
134
+ const t = (A + F + k[i] + M[g]) | 0;
135
+ A = D; D = C; C = B; B = (B + rol(t, r[i])) | 0;
136
+ }
137
+ a0 = (a0 + A) | 0; b0 = (b0 + B) | 0; c0 = (c0 + C) | 0; d0 = (d0 + D) | 0;
138
+ }
139
+ const out = new Uint8Array(16);
140
+ const ov = new DataView(out.buffer);
141
+ ov.setUint32(0, a0, true);
142
+ ov.setUint32(4, b0, true);
143
+ ov.setUint32(8, c0, true);
144
+ ov.setUint32(12, d0, true);
145
+ return out;
146
+ }
147
+
148
+ function padPassword(pw) {
149
+ if (pw === undefined || pw === null) pw = '';
150
+ if (typeof pw === 'string') pw = new TextEncoder().encode(pw);
151
+ if (!(pw instanceof Uint8Array)) {
152
+ throw new EncryptionError('pdf/crypto/v4/bad-password',
153
+ 'password must be string or Uint8Array');
154
+ }
155
+ const out = new Uint8Array(32);
156
+ const take = Math.min(32, pw.length);
157
+ out.set(pw.subarray(0, take));
158
+ out.set(PASSWORD_PADDING.subarray(0, 32 - take), take);
159
+ return out;
160
+ }
161
+
162
+ // §7.6.3.3 Algorithm 2 — compute file encryption key for R=4.
163
+ // params: { O, P, idFirst, encryptMetadata }
164
+ function computeFileKey(pw, O, P, idFirst, encryptMetadata) {
165
+ if (!(O instanceof Uint8Array) || O.length !== 32) {
166
+ throw new EncryptionError('pdf/crypto/v4/bad-O',
167
+ '/O must be a 32-byte Uint8Array');
168
+ }
169
+ if (!(idFirst instanceof Uint8Array)) {
170
+ throw new EncryptionError('pdf/crypto/v4/bad-id',
171
+ 'first /ID element required');
172
+ }
173
+ const padded = padPassword(pw);
174
+ const extra = encryptMetadata === false ? 4 : 0;
175
+ const buf = new Uint8Array(32 + 32 + 4 + idFirst.length + extra);
176
+ let p = 0;
177
+ buf.set(padded, p); p += 32;
178
+ buf.set(O, p); p += 32;
179
+ const dv = new DataView(buf.buffer);
180
+ dv.setInt32(p, P | 0, true); p += 4;
181
+ buf.set(idFirst, p); p += idFirst.length;
182
+ if (extra) { buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p] = 0xFF; }
183
+ let h = md5(buf);
184
+ const nBytes = 16; // 128-bit
185
+ for (let i = 0; i < 50; i++) h = md5(h.subarray(0, nBytes));
186
+ return h.subarray(0, nBytes);
187
+ }
188
+
189
+ // §7.6.3.4 Algorithm 5 — compute /U for R>=3.
190
+ function computeU(fileKey, idFirst) {
191
+ const seed = new Uint8Array(PASSWORD_PADDING.length + idFirst.length);
192
+ seed.set(PASSWORD_PADDING, 0);
193
+ seed.set(idFirst, PASSWORD_PADDING.length);
194
+ let h = md5(seed);
195
+ h = rc4(fileKey, h);
196
+ for (let i = 1; i <= 19; i++) {
197
+ const xk = new Uint8Array(fileKey.length);
198
+ for (let n = 0; n < fileKey.length; n++) xk[n] = fileKey[n] ^ i;
199
+ h = rc4(xk, h);
200
+ }
201
+ const out = new Uint8Array(32);
202
+ out.set(h, 0);
203
+ return out;
204
+ }
205
+
206
+ // §7.6.3.4 Algorithm 3 — compute /O from owner+user passwords.
207
+ function computeO(ownerPw, userPw) {
208
+ const oPad = padPassword(ownerPw && ownerPw.length ? ownerPw : userPw);
209
+ let h = md5(oPad);
210
+ for (let i = 0; i < 50; i++) h = md5(h);
211
+ const rc4Key = h.subarray(0, 16);
212
+ let out = rc4(rc4Key, padPassword(userPw));
213
+ for (let i = 1; i <= 19; i++) {
214
+ const xk = new Uint8Array(16);
215
+ for (let n = 0; n < 16; n++) xk[n] = rc4Key[n] ^ i;
216
+ out = rc4(xk, out);
217
+ }
218
+ return out;
219
+ }
220
+
221
+ function bytesEqual(a, b, n) {
222
+ const len = n === undefined ? Math.min(a.length, b.length) : n;
223
+ for (let i = 0; i < len; i++) if (a[i] !== b[i]) return false;
224
+ return true;
225
+ }
226
+
227
+ // Per-object key derivation for V=4. `isAes=true` appends "sAlT".
228
+ function objectKey(fileKey, objNum, gen, isAes) {
229
+ const extra = isAes ? 4 : 0;
230
+ const buf = new Uint8Array(fileKey.length + 5 + extra);
231
+ buf.set(fileKey, 0);
232
+ buf[fileKey.length + 0] = objNum & 0xFF;
233
+ buf[fileKey.length + 1] = (objNum >> 8) & 0xFF;
234
+ buf[fileKey.length + 2] = (objNum >> 16) & 0xFF;
235
+ buf[fileKey.length + 3] = gen & 0xFF;
236
+ buf[fileKey.length + 4] = (gen >> 8) & 0xFF;
237
+ if (isAes) buf.set(AES_SALT, fileKey.length + 5);
238
+ const h = md5(buf);
239
+ return h.subarray(0, Math.min(fileKey.length + 5, 16));
240
+ }
241
+
242
+ // ---- AES-128-CBC primitives ----
243
+
244
+ function aesCbcDecrypt(key, iv, ciphertext) {
245
+ if (ciphertext.length % 16 !== 0) {
246
+ throw new EncryptionError('pdf/crypto/v4/bad-ciphertext-len',
247
+ 'AES-CBC ciphertext length must be a multiple of 16');
248
+ }
249
+ const cipher = aes.fn(bytesToWords(key, 0, key.length), true);
250
+ if (cipher === false) {
251
+ throw new EncryptionError('pdf/crypto/v4/aes-schedule-failed',
252
+ 'AES-128 key schedule failed');
253
+ }
254
+ const ctWords = bytesToWords(ciphertext, 0, ciphertext.length);
255
+ const ivWords = bytesToWords(iv, 0, 16);
256
+ const ptWords = cbc.decrypt(cipher, ctWords, ivWords);
257
+ if (ptWords === false) {
258
+ throw new EncryptionError('pdf/crypto/v4/cbc-decrypt-failed',
259
+ 'AES-128-CBC decryption failed');
260
+ }
261
+ const out = new Uint8Array(ciphertext.length);
262
+ wordsToBytes(ptWords, out, 0);
263
+ return out;
264
+ }
265
+
266
+ function aesCbcEncrypt(key, iv, plaintext) {
267
+ if (plaintext.length % 16 !== 0) {
268
+ throw new EncryptionError('pdf/crypto/v4/bad-pt-len',
269
+ 'AES-CBC plaintext length must be a multiple of 16');
270
+ }
271
+ const cipher = aes.fn(bytesToWords(key, 0, key.length), false);
272
+ if (cipher === false || typeof cipher.encrypt !== 'function') {
273
+ throw new EncryptionError('pdf/crypto/v4/aes-encrypt-schedule',
274
+ 'AES-128 key schedule failed for encrypt');
275
+ }
276
+ const ptWords = bytesToWords(plaintext, 0, plaintext.length);
277
+ const ivWords = bytesToWords(iv, 0, 16);
278
+ const ctWords = cbc.encrypt(cipher, ptWords, ivWords);
279
+ if (ctWords === false) {
280
+ throw new EncryptionError('pdf/crypto/v4/cbc-encrypt-failed',
281
+ 'AES-128-CBC encryption failed');
282
+ }
283
+ const out = new Uint8Array(plaintext.length);
284
+ wordsToBytes(ctWords, out, 0);
285
+ return out;
286
+ }
287
+
288
+ function padPkcs7(buf) {
289
+ const pad = 16 - (buf.length % 16);
290
+ const out = new Uint8Array(buf.length + pad);
291
+ out.set(buf);
292
+ for (let i = buf.length; i < out.length; i++) out[i] = pad;
293
+ return out;
294
+ }
295
+ function stripPkcs7(buf) {
296
+ if (buf.length === 0) return buf;
297
+ const pad = buf[buf.length - 1];
298
+ if (pad < 1 || pad > 16 || pad > buf.length) return buf;
299
+ for (let i = buf.length - pad; i < buf.length; i++) {
300
+ if (buf[i] !== pad) return buf;
301
+ }
302
+ return buf.subarray(0, buf.length - pad);
303
+ }
304
+
305
+ // ---- READ side ----
306
+
307
+ // typedEncrypt minimal shape: { O, U, P, idFirst, encryptMetadata, method }
308
+ function tryPassword(typedEncrypt, password, isOwner) {
309
+ const O = typedEncrypt.O, U = typedEncrypt.U;
310
+ const idFirst = typedEncrypt.idFirst;
311
+ const P = typedEncrypt.P | 0;
312
+ const encryptMetadata = typedEncrypt.EncryptMetadata !== false;
313
+ if (!(O instanceof Uint8Array) || O.length !== 32 ||
314
+ !(U instanceof Uint8Array) || U.length !== 32) {
315
+ throw new EncryptionError('pdf/crypto/v4/bad-O-U',
316
+ '/O and /U must be 32 bytes for v4');
317
+ }
318
+ if (!(idFirst instanceof Uint8Array)) {
319
+ throw new EncryptionError('pdf/crypto/v4/bad-id',
320
+ 'idFirst is required for v4 key derivation');
321
+ }
322
+ let userPw;
323
+ if (isOwner) {
324
+ // Recover user password from /O.
325
+ const oPad = padPassword(password);
326
+ let h = md5(oPad);
327
+ for (let i = 0; i < 50; i++) h = md5(h);
328
+ const rc4Key = h.subarray(0, 16);
329
+ let recovered = O;
330
+ for (let i = 19; i >= 0; i--) {
331
+ const xk = new Uint8Array(16);
332
+ for (let n = 0; n < 16; n++) xk[n] = rc4Key[n] ^ i;
333
+ recovered = rc4(xk, recovered);
334
+ }
335
+ userPw = recovered;
336
+ } else {
337
+ userPw = password;
338
+ }
339
+ const fk = computeFileKey(userPw, O, P, idFirst, encryptMetadata);
340
+ const u = computeU(fk, idFirst);
341
+ const ok = bytesEqual(u, U, 16);
342
+ return ok ? { fileEncryptionKey: fk } : { fileEncryptionKey: null };
343
+ }
344
+
345
+ function decryptString(typedEncrypt, fek, objNum, gen, ciphertext) {
346
+ const method = typedEncrypt && typedEncrypt.method;
347
+ if (method === 'AESV2') {
348
+ if (ciphertext.length < 16) {
349
+ throw new EncryptionError('pdf/crypto/v4/bad-string',
350
+ 'AESV2 string must be at least 16 bytes (IV)');
351
+ }
352
+ const key = objectKey(fek, objNum, gen, true);
353
+ const iv = ciphertext.subarray(0, 16);
354
+ const body = ciphertext.subarray(16);
355
+ if (body.length === 0) return body;
356
+ const pt = aesCbcDecrypt(key, iv, body);
357
+ return stripPkcs7(pt);
358
+ }
359
+ if (method === 'V2' || method === 'RC4') {
360
+ const key = objectKey(fek, objNum, gen, false);
361
+ return rc4(key, ciphertext);
362
+ }
363
+ throw new EncryptionError('pdf/crypto/v4/bad-method',
364
+ 'V4 method must be AESV2 or V2',
365
+ { context: { method } });
366
+ }
367
+ function decryptStream(typedEncrypt, fek, objNum, gen, ciphertext) {
368
+ return decryptString(typedEncrypt, fek, objNum, gen, ciphertext);
369
+ }
370
+ // Embedded file stream (ISO 32000-1 §7.6.5): same per-object key
371
+ // derivation as a regular stream, but the dispatched method comes
372
+ // from /EFF. Callers pass a typed view whose `.method` is the
373
+ // resolved `effMethod`.
374
+ function decryptEmbeddedFile(typedEncrypt, fek, objNum, gen, ciphertext) {
375
+ return decryptString(typedEncrypt, fek, objNum, gen, ciphertext);
376
+ }
377
+ function encryptEmbeddedFile(typedEncrypt, fek, objNum, gen, plaintext, iv) {
378
+ return encryptString(typedEncrypt, fek, objNum, gen, plaintext, iv);
379
+ }
380
+
381
+ // ---- WRITE side ----
382
+
383
+ function encryptString(typedEncrypt, fek, objNum, gen, plaintext, iv) {
384
+ const method = typedEncrypt && typedEncrypt.method;
385
+ if (!(plaintext instanceof Uint8Array)) {
386
+ throw new EncryptionError('pdf/crypto/v4/encrypt-bad-input',
387
+ 'plaintext must be Uint8Array');
388
+ }
389
+ if (method === 'AESV2') {
390
+ if (!(iv instanceof Uint8Array) || iv.length !== 16) {
391
+ throw new EncryptionError('pdf/crypto/v4/encrypt-bad-iv',
392
+ 'AESV2 IV must be 16 bytes');
393
+ }
394
+ const key = objectKey(fek, objNum, gen, true);
395
+ const padded = padPkcs7(plaintext);
396
+ const body = aesCbcEncrypt(key, iv, padded);
397
+ const out = new Uint8Array(16 + body.length);
398
+ out.set(iv, 0);
399
+ out.set(body, 16);
400
+ return out;
401
+ }
402
+ if (method === 'V2' || method === 'RC4') {
403
+ const key = objectKey(fek, objNum, gen, false);
404
+ return rc4(key, plaintext);
405
+ }
406
+ throw new EncryptionError('pdf/crypto/v4/bad-method',
407
+ 'V4 method must be AESV2 or V2',
408
+ { context: { method } });
409
+ }
410
+ function encryptStream(typedEncrypt, fek, objNum, gen, plaintext, iv) {
411
+ return encryptString(typedEncrypt, fek, objNum, gen, plaintext, iv);
412
+ }
413
+
414
+ // Build /O and /U pair given owner + user passwords + idFirst.
415
+ // Returns { O, U, fek } where fek is the derived 16-byte file key.
416
+ function buildOU(ownerPassword, userPassword, P, idFirst, encryptMetadata) {
417
+ if (!(idFirst instanceof Uint8Array)) {
418
+ throw new EncryptionError('pdf/crypto/v4/bad-id',
419
+ 'idFirst required to build /O and /U');
420
+ }
421
+ const O = computeO(ownerPassword, userPassword);
422
+ const fek = computeFileKey(userPassword, O, P, idFirst, encryptMetadata);
423
+ const U = computeU(fek, idFirst);
424
+ return { O, U, fek };
425
+ }
426
+
427
+ // Algorithm 8 (V=4) — /Perms is NOT used for R=4 (only R>=5). We
428
+ // expose a no-op for symmetry; callers should not emit /Perms
429
+ // for V=4.
430
+ function buildPerms() { return null; }
431
+
432
+ return {
433
+ tryPassword,
434
+ decryptString, decryptStream,
435
+ decryptEmbeddedFile, encryptEmbeddedFile,
436
+ encryptString, encryptStream,
437
+ buildOU, buildPerms,
438
+ computeFileKey, computeU, computeO,
439
+ objectKey, rc4, md5, padPassword,
440
+ PASSWORD_PADDING
441
+ };
442
+ }
443
+ };