pdf-lite 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 (206) hide show
  1. package/.commitlintrc.cjs +25 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. package/.github/workflows/docs.yaml +93 -0
  5. package/.github/workflows/prepare-release.yaml +79 -0
  6. package/.github/workflows/release.yaml +80 -0
  7. package/.github/workflows/test.yaml +35 -0
  8. package/.husky/commit-msg +1 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.prettierignore +4 -0
  11. package/.prettierrc +4 -0
  12. package/CONTRIBUTING.md +109 -0
  13. package/EXAMPLES.md +1515 -0
  14. package/LICENSE +21 -0
  15. package/README.md +285 -0
  16. package/examples/001-create-pdf.ts +112 -0
  17. package/examples/002-create-encrypted-pdf.ts +121 -0
  18. package/examples/003-sign-pdf.ts +347 -0
  19. package/examples/004-incremental-update.ts +206 -0
  20. package/examples/005-modify-acroform.ts +374 -0
  21. package/examples/006-tokeniser-example.ts +131 -0
  22. package/examples/007-decoder-example.ts +197 -0
  23. package/package.json +72 -0
  24. package/packages/pdf-lite/README.md +3 -0
  25. package/packages/pdf-lite/package.json +68 -0
  26. package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
  27. package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
  28. package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
  29. package/packages/pdf-lite/src/core/decoder.ts +454 -0
  30. package/packages/pdf-lite/src/core/generators.ts +128 -0
  31. package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
  32. package/packages/pdf-lite/src/core/index.ts +2 -0
  33. package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
  34. package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
  35. package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
  36. package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
  37. package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
  38. package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
  39. package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
  40. package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
  41. package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
  42. package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
  43. package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
  44. package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
  45. package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
  46. package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
  47. package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
  48. package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
  49. package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
  50. package/packages/pdf-lite/src/core/parser.ts +22 -0
  51. package/packages/pdf-lite/src/core/ref.ts +102 -0
  52. package/packages/pdf-lite/src/core/serializer.ts +68 -0
  53. package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
  54. package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
  55. package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
  56. package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
  57. package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
  58. package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
  59. package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
  60. package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
  61. package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
  62. package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
  63. package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
  64. package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
  65. package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
  66. package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
  67. package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
  68. package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
  69. package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
  70. package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
  71. package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
  72. package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
  73. package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
  74. package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
  75. package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
  76. package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
  77. package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
  78. package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
  79. package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
  80. package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
  81. package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
  82. package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
  83. package/packages/pdf-lite/src/crypto/constants.ts +10 -0
  84. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
  85. package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
  86. package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
  87. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
  88. package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
  89. package/packages/pdf-lite/src/crypto/types.ts +6 -0
  90. package/packages/pdf-lite/src/crypto/utils.ts +81 -0
  91. package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
  92. package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
  93. package/packages/pdf-lite/src/filters/flate.ts +39 -0
  94. package/packages/pdf-lite/src/filters/lzw.ts +144 -0
  95. package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
  96. package/packages/pdf-lite/src/filters/runlength.ts +92 -0
  97. package/packages/pdf-lite/src/filters/types.ts +21 -0
  98. package/packages/pdf-lite/src/index.ts +4 -0
  99. package/packages/pdf-lite/src/pdf/errors.ts +5 -0
  100. package/packages/pdf-lite/src/pdf/index.ts +4 -0
  101. package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
  102. package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
  103. package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
  104. package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
  105. package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
  106. package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
  107. package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
  108. package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
  109. package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
  110. package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
  111. package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
  112. package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
  113. package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
  114. package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
  115. package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
  116. package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
  117. package/packages/pdf-lite/src/security/types.ts +158 -0
  118. package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
  119. package/packages/pdf-lite/src/signing/index.ts +3 -0
  120. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
  121. package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
  122. package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
  123. package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
  124. package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
  125. package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
  126. package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
  127. package/packages/pdf-lite/src/signing/signer.ts +120 -0
  128. package/packages/pdf-lite/src/signing/types.ts +86 -0
  129. package/packages/pdf-lite/src/signing/utils.ts +71 -0
  130. package/packages/pdf-lite/src/types.ts +44 -0
  131. package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
  132. package/packages/pdf-lite/src/utils/algos.ts +446 -0
  133. package/packages/pdf-lite/src/utils/assert.ts +42 -0
  134. package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
  135. package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
  136. package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
  137. package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
  138. package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
  139. package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
  140. package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
  141. package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
  142. package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
  143. package/packages/pdf-lite/src/utils/predictors.ts +332 -0
  144. package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
  145. package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
  146. package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
  147. package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
  148. package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
  149. package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
  150. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
  151. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
  152. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
  153. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
  154. package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
  155. package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
  156. package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
  157. package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
  158. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
  159. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
  160. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
  161. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
  162. package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
  163. package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
  164. package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
  165. package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
  166. package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
  167. package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
  168. package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
  169. package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
  170. package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
  171. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
  172. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
  173. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
  174. package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
  175. package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
  176. package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
  177. package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
  178. package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
  179. package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
  180. package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
  181. package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
  182. package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
  183. package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
  184. package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
  185. package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
  186. package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
  187. package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
  188. package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
  189. package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
  190. package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
  191. package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
  192. package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
  193. package/packages/pdf-lite/tsconfig.json +4 -0
  194. package/packages/pdf-lite/tsconfig.prod.json +8 -0
  195. package/packages/pdf-lite/typedoc.json +14 -0
  196. package/packages/pdf-lite/vitest.config.ts +43 -0
  197. package/pnpm-workspace.yaml +2 -0
  198. package/renovate.json +34 -0
  199. package/scripts/build-examples.ts +30 -0
  200. package/scripts/bump-version.sh +56 -0
  201. package/scripts/gen-html-docs.sh +21 -0
  202. package/scripts/gen-md-docs.sh +15 -0
  203. package/scripts/prepare-release.sh +33 -0
  204. package/tsconfig.json +22 -0
  205. package/tsconfig.prod.json +12 -0
  206. package/typedoc.json +34 -0
@@ -0,0 +1,158 @@
1
+ import { PdfArray } from '../core/objects/pdf-array'
2
+ import { PdfBoolean } from '../core/objects/pdf-boolean'
3
+ import { PdfDictionary } from '../core/objects/pdf-dictionary'
4
+ import { PdfHexadecimal } from '../core/objects/pdf-hexadecimal'
5
+ import { PdfIndirectObject } from '../core/objects/pdf-indirect-object'
6
+ import { PdfName } from '../core/objects/pdf-name'
7
+ import { PdfNumber } from '../core/objects/pdf-number'
8
+ import { PdfString } from '../core/objects/pdf-string'
9
+ import { ByteArray } from '../types'
10
+
11
+ /**
12
+ * Represents the PDF document ID array containing two hexadecimal identifiers.
13
+ * The first element is the permanent ID assigned when the document is created,
14
+ * and the second is updated when the document is modified.
15
+ */
16
+ export type PdfId = PdfArray<PdfHexadecimal>
17
+
18
+ /**
19
+ * Dictionary defining a crypt filter for PDF encryption.
20
+ * Specifies the encryption method and authentication event trigger.
21
+ */
22
+ export type PdfCryptFilterDictionary = PdfDictionary<{
23
+ AuthEvent: PdfName<'DocOpen' | 'EFOpen'>
24
+ CFM: PdfName<'None' | 'V2' | 'AESV2' | 'AESV3'>
25
+ Length?: PdfNumber
26
+ }>
27
+
28
+ /**
29
+ * The encryption dictionary stored in the PDF trailer.
30
+ * Contains all encryption parameters including filter type, version, revision,
31
+ * owner/user keys, permissions, and crypt filter configurations.
32
+ */
33
+ export type PdfEncryptionDictionary = PdfDictionary<{
34
+ Filter: PdfName<'Standard' | 'Adobe.PubSec'>
35
+ SubFilter?: PdfName<'adbe.pkcs7.s3' | 'adbe.pkcs7.s4' | 'adbe.pkcs7.s5'>
36
+ V: PdfNumber
37
+ R: PdfNumber
38
+ O: PdfString | PdfHexadecimal
39
+ OE?: PdfString | PdfHexadecimal
40
+ U: PdfString | PdfHexadecimal
41
+ UE?: PdfString | PdfHexadecimal
42
+ P: PdfNumber
43
+ Perms?: PdfString | PdfHexadecimal
44
+ Length: PdfNumber
45
+ CF?: PdfDictionary<{
46
+ [key: string]: PdfCryptFilterDictionary
47
+ }>
48
+ StmF?: PdfName
49
+ StrF?: PdfName
50
+ EFF?: PdfName
51
+ EncryptMetadata?: PdfBoolean
52
+ ID?: PdfArray<PdfHexadecimal>
53
+ Recipients?: PdfArray<PdfHexadecimal>
54
+ }>
55
+
56
+ /**
57
+ * An indirect object containing the encryption dictionary.
58
+ */
59
+ export type PdfEncryptionDictionaryObject =
60
+ PdfIndirectObject<PdfEncryptionDictionary>
61
+
62
+ /**
63
+ * Specifies the type of content being encrypted.
64
+ * - 'string': PDF string objects
65
+ * - 'stream': PDF stream objects
66
+ * - 'file': Embedded file streams
67
+ */
68
+ export type CryptFilterType = 'string' | 'stream' | 'file'
69
+
70
+ /**
71
+ * Recipient information for public key encryption.
72
+ * Contains the certificate for encryption and optional private key for decryption.
73
+ */
74
+ export type PdfEncryptionRecipient = {
75
+ certificate?: ByteArray
76
+ privateKey?: ByteArray
77
+ }
78
+
79
+ /**
80
+ * Supported encryption algorithm types.
81
+ * - 'RC4-40': 40-bit RC4 encryption (weak, legacy)
82
+ * - 'RC4-128': 128-bit RC4 encryption (legacy)
83
+ * - 'AES-128-CBC': 128-bit AES in CBC mode
84
+ * - 'AES-256-CBC': 256-bit AES in CBC mode (recommended)
85
+ * - 'none': No encryption (identity filter)
86
+ */
87
+ export type PdfEncryptionAlgorithmType =
88
+ | 'RC4-40'
89
+ | 'RC4-128'
90
+ | 'AES-128-CBC'
91
+ | 'AES-256-CBC'
92
+ | 'none'
93
+
94
+ /**
95
+ * RC4-40 encryption configuration.
96
+ */
97
+ type Rc40 = {
98
+ default: 'RC4-40'
99
+ }
100
+
101
+ /**
102
+ * RC4-128 encryption configuration.
103
+ */
104
+ type Rc128 = {
105
+ default: 'RC4-128'
106
+ }
107
+
108
+ /**
109
+ * AES-128 encryption configuration with optional per-type crypt filters.
110
+ */
111
+ type Aes128WithCryptFilters = {
112
+ default: 'AES-128-CBC'
113
+ streams?: 'AES-128-CBC' | 'RC4-128' | 'RC4-40' | 'none'
114
+ strings?: 'AES-128-CBC' | 'RC4-128' | 'RC4-40' | 'none'
115
+ }
116
+
117
+ /**
118
+ * AES-256 encryption configuration with optional per-type crypt filters.
119
+ */
120
+ type Aes256 = {
121
+ default: 'AES-256-CBC' | 'none'
122
+ streams?: 'AES-256-CBC' | 'AES-128-CBC' | 'RC4-128' | 'none'
123
+ files?: 'AES-256-CBC' | 'AES-128-CBC' | 'RC4-128' | 'none'
124
+ strings?: 'AES-256-CBC' | 'AES-128-CBC' | 'RC4-128' | 'none'
125
+ }
126
+
127
+ /**
128
+ * Union of all encryption algorithm configuration options.
129
+ */
130
+ export type PdfEncryptionAlgorithmOptions =
131
+ | Rc40
132
+ | Rc128
133
+ | Aes128WithCryptFilters
134
+ | Aes256
135
+
136
+ /**
137
+ * Options for configuring PDF encryption.
138
+ * Includes encryption method, passwords, document ID, and permission settings.
139
+ */
140
+ export type PdfEncryptionOptions = {
141
+ method?: PdfEncryptionAlgorithmOptions
142
+ password?: ByteArray | string
143
+ ownerPassword?: ByteArray | string
144
+ documentId?: PdfId | ByteArray | string
145
+ encryptMetadata?: boolean
146
+ recipients?: PdfEncryptionRecipient[]
147
+ permissions?: {
148
+ print?: boolean
149
+ modify?: boolean
150
+ copy?: boolean
151
+ annotate?: boolean
152
+ fill?: boolean
153
+ extract?: boolean
154
+ assemble?: boolean
155
+ printHighQuality?: boolean
156
+ all?: boolean
157
+ }
158
+ }
@@ -0,0 +1,224 @@
1
+ import { PdfArray } from '../core/objects/pdf-array'
2
+ import { PdfDate } from '../core/objects/pdf-date'
3
+ import { PdfDictionary } from '../core/objects/pdf-dictionary'
4
+ import { PdfIndirectObject } from '../core/objects/pdf-indirect-object'
5
+ import { PdfName } from '../core/objects/pdf-name'
6
+ import { PdfObjectReference } from '../core/objects/pdf-object-reference'
7
+ import { PdfStream } from '../core/objects/pdf-stream'
8
+ import { PdfString } from '../core/objects/pdf-string'
9
+ import { PdfDocument } from '../pdf/pdf-document'
10
+ import { ByteArray } from '../types'
11
+ import { RevocationInfo } from './types'
12
+
13
+ /**
14
+ * Indirect object containing a certificate stream.
15
+ */
16
+ export class PdfCertObject extends PdfIndirectObject<PdfStream> {}
17
+
18
+ /**
19
+ * Indirect object containing a CRL stream.
20
+ */
21
+ export class PdfCrlObject extends PdfIndirectObject<PdfStream> {}
22
+
23
+ /**
24
+ * Indirect object containing an OCSP response stream.
25
+ */
26
+ export class PdfOcspObject extends PdfIndirectObject<PdfStream> {}
27
+
28
+ /**
29
+ * Validation Related Information (VRI) dictionary.
30
+ * Associates revocation data with specific certificate hashes.
31
+ */
32
+ export type PdfVriObject = PdfIndirectObject<
33
+ PdfDictionary<{
34
+ [CertHash: string]: PdfDictionary<{
35
+ Type?: PdfName<'VRI'>
36
+ Cert?: PdfObjectReference
37
+ OCSP?: PdfObjectReference
38
+ CRL?: PdfObjectReference
39
+ TU?: PdfDate
40
+ TS?: PdfString
41
+ }>
42
+ }>
43
+ >
44
+
45
+ /**
46
+ * Dictionary for the Document Security Store (DSS).
47
+ * Contains arrays of certificates, CRLs, OCSPs, and VRI entries.
48
+ */
49
+ export class PdfDocumentSecurityStoreDictionary extends PdfDictionary<{
50
+ Type?: PdfName<'DSS'>
51
+ VRI?: PdfObjectReference
52
+ OCSPs?: PdfArray<PdfObjectReference>
53
+ CRLs?: PdfArray<PdfObjectReference>
54
+ Certs?: PdfArray<PdfObjectReference>
55
+ }> {}
56
+
57
+ /**
58
+ * Document Security Store (DSS) object for PAdES LTV signatures.
59
+ * Stores validation data (certificates, CRLs, OCSPs) for long-term validation.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const dss = new PdfDocumentSecurityStoreObject(document)
64
+ * await dss.addCert(certificateBytes)
65
+ * await dss.addCrl(crlBytes)
66
+ * await dss.addOcsp(ocspBytes)
67
+ * ```
68
+ */
69
+ export class PdfDocumentSecurityStoreObject extends PdfIndirectObject<PdfDocumentSecurityStoreDictionary> {
70
+ /** Reference to the parent document. */
71
+ private document: PdfDocument
72
+
73
+ /**
74
+ * Creates a new DSS object.
75
+ *
76
+ * @param document - The parent PDF document.
77
+ * @param content - Optional pre-existing DSS dictionary.
78
+ */
79
+ constructor(
80
+ document: PdfDocument,
81
+ content?: PdfDocumentSecurityStoreDictionary,
82
+ ) {
83
+ super(content ?? new PdfDocumentSecurityStoreDictionary())
84
+ this.document = document
85
+ }
86
+
87
+ /**
88
+ * Adds an OCSP response to the DSS, avoiding duplicates.
89
+ *
90
+ * @param ocsp - The DER-encoded OCSP response.
91
+ * @returns The created or existing OCSP object.
92
+ */
93
+ async addOcsp(ocsp: ByteArray): Promise<PdfOcspObject> {
94
+ const newOcsp = new PdfStream(ocsp)
95
+ let ocspArray = this.content.get('OCSPs')
96
+
97
+ const currentOcsps = (await Promise.all(
98
+ (ocspArray?.items ?? []).map(async (ref) => {
99
+ const obj = await this.document.readObject(ref)
100
+ return obj
101
+ }),
102
+ )) as PdfIndirectObject<PdfStream>[]
103
+
104
+ for (const existingOcsp of currentOcsps) {
105
+ if (existingOcsp.content.equals(newOcsp)) {
106
+ return existingOcsp as PdfOcspObject
107
+ }
108
+ }
109
+
110
+ const ocspObject = new PdfOcspObject(newOcsp)
111
+ this.document.add(ocspObject)
112
+
113
+ if (!ocspArray) {
114
+ ocspArray = new PdfArray<PdfObjectReference>([])
115
+ this.content.set('OCSPs', ocspArray)
116
+ }
117
+
118
+ ocspArray.items.push(ocspObject.reference)
119
+
120
+ return ocspObject
121
+ }
122
+
123
+ /**
124
+ * Adds a CRL to the DSS, avoiding duplicates.
125
+ *
126
+ * @param crl - The DER-encoded CRL.
127
+ * @returns The created or existing CRL object.
128
+ */
129
+ async addCrl(crl: ByteArray): Promise<PdfCrlObject> {
130
+ let crlArray = this.content.get('CRLs')
131
+
132
+ const currentCrls = (await Promise.all(
133
+ (crlArray?.items ?? []).map(async (ref) => {
134
+ const obj = await this.document.readObject(ref)
135
+ return obj
136
+ }),
137
+ )) as PdfIndirectObject<PdfStream>[]
138
+
139
+ for (const existingCrl of currentCrls) {
140
+ if (existingCrl.content.equals(new PdfStream(crl))) {
141
+ return existingCrl as PdfCrlObject
142
+ }
143
+ }
144
+
145
+ const crlObject = new PdfCrlObject(new PdfStream(crl))
146
+ this.document.add(crlObject)
147
+
148
+ if (!crlArray) {
149
+ crlArray = new PdfArray<PdfObjectReference>([])
150
+ this.content.set('CRLs', crlArray)
151
+ }
152
+
153
+ crlArray.items.push(crlObject.reference)
154
+
155
+ return crlObject
156
+ }
157
+
158
+ /**
159
+ * Adds a certificate to the DSS, avoiding duplicates.
160
+ *
161
+ * @param cert - The DER-encoded certificate.
162
+ * @returns The created or existing certificate object.
163
+ */
164
+ async addCert(cert: ByteArray): Promise<PdfCertObject> {
165
+ let certArray = this.content.get('Certs')
166
+
167
+ const currentCerts = (await Promise.all(
168
+ (certArray?.items ?? []).map(async (ref) => {
169
+ const obj = await this.document.readObject(ref)
170
+ return obj
171
+ }),
172
+ )) as PdfIndirectObject<PdfStream>[]
173
+
174
+ for (const existingCert of currentCerts) {
175
+ if (existingCert.content.equals(new PdfStream(cert))) {
176
+ return existingCert as PdfCertObject
177
+ }
178
+ }
179
+
180
+ const certObject = new PdfCertObject(new PdfStream(cert))
181
+ this.document.add(certObject)
182
+
183
+ if (!certArray) {
184
+ certArray = new PdfArray<PdfObjectReference>([])
185
+ this.content.set('Certs', certArray)
186
+ }
187
+
188
+ certArray.items.push(certObject.reference)
189
+
190
+ return certObject
191
+ }
192
+
193
+ /**
194
+ * Adds revocation information (CRLs and OCSPs) to the DSS.
195
+ *
196
+ * @param revocationInfo - The revocation information to add.
197
+ */
198
+ async addRevocationInfo(revocationInfo: RevocationInfo): Promise<void> {
199
+ for (const ocsp of revocationInfo.ocsps ?? []) {
200
+ await this.addOcsp(ocsp)
201
+ }
202
+
203
+ for (const crl of revocationInfo.crls ?? []) {
204
+ await this.addCrl(crl)
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Checks if the DSS is empty (contains no certificates, CRLs, or OCSPs).
210
+ *
211
+ * @returns True if the DSS has no stored data.
212
+ */
213
+ isEmpty(): boolean {
214
+ const certs = this.content.get('Certs')
215
+ const crls = this.content.get('CRLs')
216
+ const ocsps = this.content.get('OCSPs')
217
+
218
+ return (
219
+ (!certs || certs.items.length === 0) &&
220
+ (!crls || crls.items.length === 0) &&
221
+ (!ocsps || ocsps.items.length === 0)
222
+ )
223
+ }
224
+ }
@@ -0,0 +1,3 @@
1
+ export * from './signatures'
2
+ export * from './document-security-store'
3
+ export * from './types'
@@ -0,0 +1,154 @@
1
+ import { RevocationInfo, TimeStampAuthority } from '../types'
2
+ import { SignedData } from 'pki-lite/pkcs7/SignedData'
3
+ import { Certificate } from 'pki-lite/x509/Certificate'
4
+ import { SignerInfo } from 'pki-lite/pkcs7/SignerInfo'
5
+ import { Attribute } from 'pki-lite/x509/Attribute'
6
+ import { RevocationInfoArchival } from 'pki-lite/adobe/RevocationInfoArchival'
7
+ import { CertificateList } from 'pki-lite/x509/CertificateList'
8
+ import { OCSPResponse } from 'pki-lite/ocsp/OCSPResponse'
9
+ import { PrivateKeyInfo } from 'pki-lite/keys/PrivateKeyInfo'
10
+ import { OtherRevInfo } from 'pki-lite/adobe/OtherRevInfo'
11
+ import { AsymmetricEncryptionAlgorithmParams } from 'pki-lite/core/index'
12
+ import { fetchRevocationInfo } from '../utils'
13
+ import { PdfSignatureObject, PdfSignatureSignOptions } from './base'
14
+ import { ByteArray } from '../../types'
15
+
16
+ /**
17
+ * PKCS#7 detached signature object (adbe.pkcs7.detached).
18
+ * Creates CMS SignedData with the document hash as external data.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const signature = new PdfAdbePkcs7DetachedSignatureObject({
23
+ * privateKey: keyBytes,
24
+ * certificate: certBytes,
25
+ * reason: 'Document approval',
26
+ * timeStampAuthority: true
27
+ * })
28
+ * ```
29
+ */
30
+ export class PdfAdbePkcs7DetachedSignatureObject extends PdfSignatureObject {
31
+ /** Private key for signing. */
32
+ privateKey: ByteArray
33
+ /** Signer certificate. */
34
+ certificate: ByteArray
35
+ /** Additional certificates for chain building. */
36
+ additionalCertificates: ByteArray[]
37
+ /** Issuer certificate for OCSP requests. */
38
+ issuerCertificate?: ByteArray
39
+ /** Signing date. */
40
+ date?: Date
41
+ /** Signature algorithm parameters. */
42
+ algorithm?: AsymmetricEncryptionAlgorithmParams
43
+ /** Revocation information or 'fetch' to retrieve automatically. */
44
+ revocationInfo?: RevocationInfo | 'fetch'
45
+ /** Timestamp authority configuration. */
46
+ timeStampAuthority?: TimeStampAuthority
47
+
48
+ /**
49
+ * Creates a new PKCS#7 detached signature object.
50
+ *
51
+ * @param options - Signature configuration options.
52
+ */
53
+ constructor(
54
+ options: PdfSignatureSignOptions & {
55
+ privateKey: ByteArray
56
+ certificate: ByteArray
57
+ issuerCertificate?: ByteArray
58
+ additionalCertificates?: ByteArray[]
59
+ algorithm?: AsymmetricEncryptionAlgorithmParams
60
+ revocationInfo?: RevocationInfo | 'fetch'
61
+ timeStampAuthority?: TimeStampAuthority | true
62
+ },
63
+ ) {
64
+ super({
65
+ ...options,
66
+ subfilter: 'adbe.pkcs7.detached',
67
+ })
68
+
69
+ this.privateKey = options.privateKey
70
+ this.certificate = options.certificate
71
+ this.issuerCertificate = options.issuerCertificate
72
+ this.additionalCertificates = options.additionalCertificates || []
73
+ this.date = options.date
74
+ this.algorithm = options.algorithm
75
+ this.revocationInfo = options.revocationInfo
76
+ this.timeStampAuthority =
77
+ options.timeStampAuthority === true
78
+ ? {
79
+ url: 'http://timestamp.digicert.com',
80
+ }
81
+ : options.timeStampAuthority
82
+ }
83
+
84
+ /**
85
+ * Signs the document bytes using PKCS#7 detached format.
86
+ *
87
+ * @param options - Signing options with bytes and revocation embedding flag.
88
+ * @returns The CMS SignedData and revocation information.
89
+ */
90
+ sign: PdfSignatureObject['sign'] = async (options) => {
91
+ const { bytes } = options
92
+
93
+ const certificate: Certificate = Certificate.fromDer(this.certificate)
94
+ const additionalCertificates: Certificate[] =
95
+ this.additionalCertificates.map(Certificate.fromDer)
96
+
97
+ const signedAttributes = new SignerInfo.SignedAttributes()
98
+ const unsignedAttributes = new SignerInfo.UnsignedAttributes()
99
+
100
+ signedAttributes.push(Attribute.signingTime(this.date ?? new Date()))
101
+
102
+ const revocationInfo =
103
+ this.revocationInfo === 'fetch'
104
+ ? await fetchRevocationInfo({
105
+ certificates: [
106
+ this.certificate,
107
+ ...(this.additionalCertificates ?? []),
108
+ ],
109
+ issuerCertificate: this.issuerCertificate,
110
+ })
111
+ : this.revocationInfo
112
+
113
+ if (options.embedRevocationInfo && revocationInfo) {
114
+ signedAttributes.push(
115
+ Attribute.adobeRevocationInfoArchival(
116
+ new RevocationInfoArchival({
117
+ crls: revocationInfo.crls?.map((x) =>
118
+ CertificateList.fromDer(x),
119
+ ),
120
+ ocsps: revocationInfo.ocsps?.map((x) =>
121
+ OCSPResponse.fromDer(x),
122
+ ),
123
+ otherRevInfo: revocationInfo.otherRevInfo?.map(
124
+ (x) =>
125
+ new OtherRevInfo({
126
+ type: x.type,
127
+ value: x.value,
128
+ }),
129
+ ),
130
+ }),
131
+ ),
132
+ )
133
+ }
134
+
135
+ const signedData = await SignedData.builder()
136
+ .addCertificate(...additionalCertificates)
137
+ .setData(bytes)
138
+ .setDetached(true)
139
+ .addSigner({
140
+ certificate,
141
+ privateKeyInfo: PrivateKeyInfo.fromDer(this.privateKey),
142
+ encryptionAlgorithm: this.algorithm,
143
+ signedAttrs: signedAttributes,
144
+ unsignedAttrs: unsignedAttributes,
145
+ tsa: this.timeStampAuthority,
146
+ })
147
+ .build()
148
+
149
+ return {
150
+ signedBytes: signedData.toCms().toDer(),
151
+ revocationInfo,
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,161 @@
1
+ import { RevocationInfo, TimeStampAuthority } from '../types'
2
+ import { SignedData } from 'pki-lite/pkcs7/SignedData'
3
+ import { Certificate } from 'pki-lite/x509/Certificate'
4
+ import { SignerInfo } from 'pki-lite/pkcs7/SignerInfo'
5
+ import { Attribute } from 'pki-lite/x509/Attribute'
6
+ import { RevocationInfoArchival } from 'pki-lite/adobe/RevocationInfoArchival'
7
+ import { CertificateList } from 'pki-lite/x509/CertificateList'
8
+ import { OCSPResponse } from 'pki-lite/ocsp/OCSPResponse'
9
+ import { PrivateKeyInfo } from 'pki-lite/keys/PrivateKeyInfo'
10
+ import { OtherRevInfo } from 'pki-lite/adobe/OtherRevInfo'
11
+ import { AsymmetricEncryptionAlgorithmParams } from 'pki-lite/core/index'
12
+ import { fetchRevocationInfo } from '../utils'
13
+ import { PdfSignatureObject, PdfSignatureSignOptions } from './base'
14
+ import { DigestAlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier'
15
+ import { ByteArray } from '../../types'
16
+
17
+ /**
18
+ * PKCS#7 SHA-1 signature object (adbe.pkcs7.sha1).
19
+ * Creates CMS SignedData with SHA-1 hash embedded as signed content.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const signature = new PdfAdbePkcs7Sha1SignatureObject({
24
+ * privateKey: keyBytes,
25
+ * certificate: certBytes
26
+ * })
27
+ * ```
28
+ */
29
+ export class PdfAdbePkcs7Sha1SignatureObject extends PdfSignatureObject {
30
+ /** Fixed algorithm for SHA-1 signatures. */
31
+ static readonly ALGORITHM: AsymmetricEncryptionAlgorithmParams = {
32
+ type: 'RSASSA_PKCS1_v1_5',
33
+ params: {
34
+ hash: 'SHA-1',
35
+ },
36
+ }
37
+
38
+ /** Private key for signing. */
39
+ privateKey: ByteArray
40
+ /** Signer certificate. */
41
+ certificate: ByteArray
42
+ /** Additional certificates for chain building. */
43
+ additionalCertificates: ByteArray[]
44
+ /** Issuer certificate for OCSP requests. */
45
+ issuerCertificate?: ByteArray
46
+ /** Signing date. */
47
+ date?: Date
48
+ /** Revocation information or 'fetch' to retrieve automatically. */
49
+ revocationInfo?: RevocationInfo | 'fetch'
50
+ /** Timestamp authority configuration. */
51
+ timeStampAuthority?: TimeStampAuthority
52
+
53
+ /**
54
+ * Creates a new PKCS#7 SHA-1 signature object.
55
+ *
56
+ * @param options - Signature configuration options.
57
+ */
58
+ constructor(
59
+ options: PdfSignatureSignOptions & {
60
+ privateKey: ByteArray
61
+ certificate: ByteArray
62
+ issuerCertificate?: ByteArray
63
+ additionalCertificates?: ByteArray[]
64
+ algorithm?: AsymmetricEncryptionAlgorithmParams
65
+ revocationInfo?: RevocationInfo | 'fetch'
66
+ timeStampAuthority?: TimeStampAuthority | true
67
+ },
68
+ ) {
69
+ super({
70
+ ...options,
71
+ subfilter: 'adbe.pkcs7.sha1',
72
+ })
73
+
74
+ this.privateKey = options.privateKey
75
+ this.certificate = options.certificate
76
+ this.issuerCertificate = options.issuerCertificate
77
+ this.additionalCertificates = options.additionalCertificates || []
78
+ this.date = options.date
79
+ this.revocationInfo = options.revocationInfo
80
+ this.timeStampAuthority =
81
+ options.timeStampAuthority === true
82
+ ? {
83
+ url: 'http://timestamp.digicert.com',
84
+ }
85
+ : options.timeStampAuthority
86
+ }
87
+
88
+ /**
89
+ * Signs the document bytes using PKCS#7 SHA-1 format.
90
+ *
91
+ * @param options - Signing options with bytes and revocation embedding flag.
92
+ * @returns The CMS SignedData and revocation information.
93
+ */
94
+ sign: PdfSignatureObject['sign'] = async (options) => {
95
+ const { bytes } = options
96
+
97
+ const certificate: Certificate = Certificate.fromDer(this.certificate)
98
+ const additionalCertificates: Certificate[] =
99
+ this.additionalCertificates.map(Certificate.fromDer)
100
+
101
+ const signedAttributes = new SignerInfo.SignedAttributes()
102
+ const unsignedAttributes = new SignerInfo.UnsignedAttributes()
103
+
104
+ signedAttributes.push(Attribute.signingTime(this.date ?? new Date()))
105
+ const revocationInfo =
106
+ this.revocationInfo === 'fetch'
107
+ ? await fetchRevocationInfo({
108
+ certificates: [
109
+ this.certificate,
110
+ ...(this.additionalCertificates ?? []),
111
+ ],
112
+ issuerCertificate: this.issuerCertificate,
113
+ })
114
+ : this.revocationInfo
115
+
116
+ if (options.embedRevocationInfo && revocationInfo) {
117
+ signedAttributes.push(
118
+ Attribute.adobeRevocationInfoArchival(
119
+ new RevocationInfoArchival({
120
+ crls: revocationInfo.crls?.map((x) =>
121
+ CertificateList.fromDer(x),
122
+ ),
123
+ ocsps: revocationInfo.ocsps?.map((x) =>
124
+ OCSPResponse.fromDer(x),
125
+ ),
126
+ otherRevInfo: revocationInfo.otherRevInfo?.map(
127
+ (x) =>
128
+ new OtherRevInfo({
129
+ type: x.type,
130
+ value: x.value,
131
+ }),
132
+ ),
133
+ }),
134
+ ),
135
+ )
136
+ }
137
+
138
+ const digest =
139
+ await DigestAlgorithmIdentifier.digestAlgorithm('SHA-1').digest(
140
+ bytes,
141
+ )
142
+ const signedData = await SignedData.builder()
143
+ .addCertificate(...additionalCertificates)
144
+ .setData(digest)
145
+ .setDetached(false)
146
+ .addSigner({
147
+ certificate,
148
+ privateKeyInfo: PrivateKeyInfo.fromDer(this.privateKey),
149
+ encryptionAlgorithm: PdfAdbePkcs7Sha1SignatureObject.ALGORITHM,
150
+ signedAttrs: signedAttributes,
151
+ unsignedAttrs: unsignedAttributes,
152
+ tsa: this.timeStampAuthority,
153
+ })
154
+ .build()
155
+
156
+ return {
157
+ signedBytes: signedData.toCms().toDer(),
158
+ revocationInfo,
159
+ }
160
+ }
161
+ }