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,106 @@
1
+ import { PrivateKeyInfo } from 'pki-lite/keys/PrivateKeyInfo'
2
+ import { AsymmetricEncryptionAlgorithmParams } from 'pki-lite/core/index'
3
+ import { PdfSignatureObject, PdfSignatureSignOptions } from './base'
4
+ import { OctetString } from 'pki-lite/asn1/OctetString'
5
+ import { AlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier'
6
+ import { ByteArray } from '../../types'
7
+ import { RevocationInfo } from '../types'
8
+ import { fetchRevocationInfo } from '../utils'
9
+
10
+ /**
11
+ * X.509 RSA-SHA1 signature object (adbe.x509.rsa_sha1).
12
+ * Creates a raw RSA-SHA1 signature with certificates in the Cert entry.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const signature = new PdfAdbePkcsX509RsaSha1SignatureObject({
17
+ * privateKey: keyBytes,
18
+ * certificate: certBytes
19
+ * })
20
+ * ```
21
+ */
22
+ export class PdfAdbePkcsX509RsaSha1SignatureObject extends PdfSignatureObject {
23
+ /** Fixed algorithm for RSA-SHA1 signatures. */
24
+ static readonly ALGORITHM: AsymmetricEncryptionAlgorithmParams = {
25
+ type: 'RSASSA_PKCS1_v1_5',
26
+ params: {
27
+ hash: 'SHA-1',
28
+ },
29
+ }
30
+
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
+ /** Revocation information or 'fetch' to retrieve automatically. */
40
+ revocationInfo?: RevocationInfo | 'fetch'
41
+
42
+ /**
43
+ * Creates a new X.509 RSA-SHA1 signature object.
44
+ *
45
+ * @param options - Signature configuration options.
46
+ */
47
+ constructor(
48
+ options: PdfSignatureSignOptions & {
49
+ privateKey: ByteArray
50
+ certificate: ByteArray
51
+ additionalCertificates?: ByteArray[]
52
+ issuerCertificate?: ByteArray
53
+ revocationInfo?: RevocationInfo | 'fetch'
54
+ },
55
+ ) {
56
+ super({
57
+ ...options,
58
+ subfilter: 'adbe.x509.rsa_sha1',
59
+ certs: [
60
+ options.certificate,
61
+ ...(options.additionalCertificates ?? []),
62
+ ],
63
+ })
64
+
65
+ this.privateKey = options.privateKey
66
+ this.revocationInfo = options.revocationInfo
67
+ this.certificate = options.certificate
68
+ this.issuerCertificate = options.issuerCertificate
69
+ this.additionalCertificates = options.additionalCertificates
70
+ }
71
+
72
+ /**
73
+ * Signs the document bytes using RSA-SHA1 format.
74
+ *
75
+ * @param options - Signing options with bytes.
76
+ * @returns The signature bytes and revocation information.
77
+ */
78
+ sign: PdfSignatureObject['sign'] = async (options) => {
79
+ const { bytes } = options
80
+ const revocationInfo =
81
+ this.revocationInfo === 'fetch'
82
+ ? await fetchRevocationInfo({
83
+ certificates: [
84
+ this.certificate,
85
+ ...(this.additionalCertificates ?? []),
86
+ ],
87
+ issuerCertificate: this.issuerCertificate,
88
+ })
89
+ : this.revocationInfo
90
+
91
+ const privateKeyInfo = PrivateKeyInfo.fromDer(this.privateKey)
92
+ const signatureAlgorithm = AlgorithmIdentifier.signatureAlgorithm(
93
+ PdfAdbePkcsX509RsaSha1SignatureObject.ALGORITHM,
94
+ )
95
+
96
+ const signatureBytes = await signatureAlgorithm.sign(
97
+ bytes,
98
+ privateKeyInfo,
99
+ )
100
+
101
+ return {
102
+ signedBytes: new OctetString({ bytes: signatureBytes }).toDer(),
103
+ revocationInfo,
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,229 @@
1
+ import { stringToBytes } from '../../utils/stringToBytes'
2
+ import { padBytes } from '../../utils/padBytes'
3
+ import { PdfDictionary } from '../../core/objects/pdf-dictionary'
4
+ import {
5
+ PdfSignatureDictionaryEntries,
6
+ PdfSignatureSubType,
7
+ RevocationInfo,
8
+ } from '../types'
9
+ import { bytesToHexBytes } from '../../utils/bytesToHexBytes'
10
+ import { PdfArray } from '../../core/objects/pdf-array'
11
+ import { PdfNumber } from '../../core/objects/pdf-number'
12
+ import { PdfHexadecimal } from '../../core/objects/pdf-hexadecimal'
13
+ import { PdfIndirectObject } from '../../core/objects/pdf-indirect-object'
14
+ import { ByteArray } from '../../types'
15
+ import { PdfName } from '../../core/objects/pdf-name'
16
+ import { PdfString } from '../../core/objects/pdf-string'
17
+ import { PdfDate } from '../../core/objects/pdf-date'
18
+
19
+ const PLACEHOLDER_BYTES = stringToBytes('placeholder_signature_bytes')
20
+ const PADDING = 8192 * 4
21
+ const OFFSET_PADDING = 12
22
+ const PLACEHOLDER_HEX_BYTES = padBytes(
23
+ bytesToHexBytes(PLACEHOLDER_BYTES),
24
+ PADDING,
25
+ )
26
+
27
+ /**
28
+ * PDF signature dictionary containing all signature-related entries.
29
+ * Manages the ByteRange and Contents fields with appropriate placeholder sizing.
30
+ */
31
+ export class PdfSignatureDictionary extends PdfDictionary<PdfSignatureDictionaryEntries> {
32
+ /**
33
+ * Creates a new signature dictionary.
34
+ *
35
+ * @param entries - The signature dictionary entries, ByteRange and Contents are auto-populated if not provided.
36
+ */
37
+ constructor(
38
+ entries: Omit<
39
+ PdfSignatureDictionaryEntries,
40
+ 'ByteRange' | 'Contents'
41
+ > & {
42
+ ByteRange?: PdfSignatureDictionaryEntries['ByteRange']
43
+ Contents?: PdfSignatureDictionaryEntries['Contents']
44
+ },
45
+ ) {
46
+ super({
47
+ ...entries,
48
+ ByteRange:
49
+ entries.ByteRange ??
50
+ new PdfArray([
51
+ new PdfNumber({ value: 0, padTo: OFFSET_PADDING }),
52
+ new PdfNumber({ value: 0, padTo: OFFSET_PADDING }),
53
+ new PdfNumber({ value: 0, padTo: OFFSET_PADDING }),
54
+ new PdfNumber({ value: 0, padTo: OFFSET_PADDING }),
55
+ ]),
56
+ Contents:
57
+ entries.Contents ?? new PdfHexadecimal(PLACEHOLDER_HEX_BYTES),
58
+ })
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Options for signature metadata.
64
+ */
65
+ export type PdfSignatureSignOptions = {
66
+ /** Signing date. */
67
+ date?: Date
68
+ /** Signer name. */
69
+ name?: string
70
+ /** Reason for signing. */
71
+ reason?: string
72
+ /** Contact information. */
73
+ contactInfo?: string
74
+ /** Signing location. */
75
+ location?: string
76
+ }
77
+
78
+ /**
79
+ * Abstract base class for PDF signature objects.
80
+ * Subclasses implement specific signature formats (PKCS#7, CAdES, etc.).
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const signature = new PdfAdbePkcs7DetachedSignatureObject({
85
+ * privateKey,
86
+ * certificate,
87
+ * reason: 'Approval'
88
+ * })
89
+ * document.add(signature)
90
+ * await document.commit()
91
+ * ```
92
+ */
93
+ export abstract class PdfSignatureObject extends PdfIndirectObject<PdfSignatureDictionary> {
94
+ /**
95
+ * Creates a new signature object.
96
+ *
97
+ * @param content - Either a signature dictionary or options to create one.
98
+ */
99
+ constructor(
100
+ content:
101
+ | PdfSignatureDictionary
102
+ | (PdfSignatureSignOptions & {
103
+ subfilter: PdfSignatureSubType
104
+ certs?: ByteArray[]
105
+ }),
106
+ ) {
107
+ super(
108
+ content instanceof PdfSignatureDictionary
109
+ ? content
110
+ : new PdfSignatureDictionary({
111
+ Type: new PdfName('Sig'),
112
+ Filter: new PdfName('Adobe.PPKLite'),
113
+ SubFilter: new PdfName(content.subfilter),
114
+ Reason: content.reason
115
+ ? new PdfString(content.reason)
116
+ : undefined,
117
+ ContactInfo: content.contactInfo
118
+ ? new PdfString(content.contactInfo)
119
+ : undefined,
120
+ Location: content.location
121
+ ? new PdfString(content.location)
122
+ : undefined,
123
+ M: content.date ? new PdfDate(content.date) : undefined,
124
+ Name: content.name
125
+ ? new PdfString(content.name)
126
+ : undefined,
127
+ Cert: content.certs
128
+ ? new PdfArray(
129
+ content.certs.map(
130
+ (cert) => new PdfHexadecimal(cert, 'bytes'),
131
+ ),
132
+ )
133
+ : undefined,
134
+ }),
135
+ )
136
+ }
137
+
138
+ /**
139
+ * Signs the document bytes and returns the signature.
140
+ *
141
+ * @param options - Signing options including bytes to sign.
142
+ * @returns The signed bytes and optional revocation information.
143
+ */
144
+ abstract sign(options: {
145
+ bytes: ByteArray
146
+ embedRevocationInfo?: boolean
147
+ }): Promise<{
148
+ signedBytes: ByteArray
149
+ revocationInfo?: RevocationInfo
150
+ }>
151
+
152
+ /**
153
+ * Gets the signature hexadecimal content.
154
+ *
155
+ * @returns The Contents entry as hexadecimal.
156
+ * @throws Error if Contents entry is missing.
157
+ */
158
+ get signedHexadecimal(): PdfHexadecimal {
159
+ const contents = this.content.get('Contents')
160
+ if (!contents) {
161
+ throw new Error('Signature dictionary is missing Contents entry')
162
+ }
163
+
164
+ return contents
165
+ }
166
+
167
+ /**
168
+ * Gets the raw signature bytes.
169
+ *
170
+ * @returns The signature bytes.
171
+ */
172
+ get signedBytes(): ByteArray {
173
+ return this.signedHexadecimal.bytes
174
+ }
175
+
176
+ /**
177
+ * Sets the signed bytes in the signature dictionary.
178
+ *
179
+ * @param signedBytes - The signature bytes to set.
180
+ * @throws Error if Contents entry is missing.
181
+ */
182
+ setSignedBytes(signedBytes: ByteArray): void {
183
+ const contents = this.content.get('Contents')
184
+ if (!contents) {
185
+ throw new Error('Signature dictionary is missing Contents entry')
186
+ }
187
+
188
+ const newHexadecimal = PdfHexadecimal.toHexadecimal(
189
+ padBytes(signedBytes, contents.bytes.length),
190
+ )
191
+
192
+ this.content.set('Contents', newHexadecimal)
193
+ }
194
+
195
+ /**
196
+ * Sets the byte range array for the signature.
197
+ *
198
+ * @param byteRange - Array of [offset1, length1, offset2, length2].
199
+ * @throws Error if ByteRange entry is missing.
200
+ */
201
+ setByteRange(byteRange: number[]): void {
202
+ const byteRangeEntry = this.content.get('ByteRange')
203
+ if (!byteRangeEntry) {
204
+ throw new Error('Signature dictionary is missing ByteRange entry')
205
+ }
206
+
207
+ this.content.set(
208
+ 'ByteRange',
209
+ new PdfArray(
210
+ byteRange.map(
211
+ (x) =>
212
+ new PdfNumber({
213
+ value: x,
214
+ padTo: OFFSET_PADDING,
215
+ }),
216
+ ),
217
+ ),
218
+ )
219
+ }
220
+
221
+ /**
222
+ * Gets the insertion order for this object in the PDF.
223
+ *
224
+ * @returns High order value to place signature near end of document.
225
+ */
226
+ order(): number {
227
+ return PdfIndirectObject.MAX_ORDER_INDEX - 10
228
+ }
229
+ }
@@ -0,0 +1,229 @@
1
+ import {
2
+ SignaturePolicyDocument,
3
+ RevocationInfo,
4
+ TimeStampAuthority,
5
+ } from '../types'
6
+ import { SignedData } from 'pki-lite/pkcs7/SignedData'
7
+ import { Certificate } from 'pki-lite/x509/Certificate'
8
+ import { SignerInfo } from 'pki-lite/pkcs7/SignerInfo'
9
+ import { Attribute } from 'pki-lite/x509/Attribute'
10
+ import { RevocationInfoArchival } from 'pki-lite/adobe/RevocationInfoArchival'
11
+ import { CertificateList } from 'pki-lite/x509/CertificateList'
12
+ import { OCSPResponse } from 'pki-lite/ocsp/OCSPResponse'
13
+ import { PrivateKeyInfo } from 'pki-lite/keys/PrivateKeyInfo'
14
+ import { OtherRevInfo } from 'pki-lite/adobe/OtherRevInfo'
15
+ import { AsymmetricEncryptionAlgorithmParams } from 'pki-lite/core/index'
16
+ import { PdfName } from '../../core/objects/pdf-name'
17
+ import { fetchRevocationInfo } from '../utils'
18
+ import { PdfString } from '../../core/objects/pdf-string'
19
+ import { PdfDate } from '../../core/objects/pdf-date'
20
+ import {
21
+ PdfSignatureDictionary,
22
+ PdfSignatureObject,
23
+ PdfSignatureSignOptions,
24
+ } from './base'
25
+ import { SigningCertificateV2 } from 'pki-lite/x509/attributes/SigningCertificateV2'
26
+ import {
27
+ OtherHashAlgAndValue,
28
+ SignaturePolicyId,
29
+ } from 'pki-lite/x509/attributes/SignaturePolicyIdentifier'
30
+ import { DigestAlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier'
31
+ import { ByteArray } from '../../types'
32
+
33
+ /**
34
+ * ETSI CAdES detached signature object (ETSI.CAdES.detached).
35
+ * Creates CAdES-compliant signatures with enhanced attributes.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const signature = new PdfEtsiCadesDetachedSignatureObject({
40
+ * privateKey: keyBytes,
41
+ * certificate: certBytes,
42
+ * reason: 'Approval',
43
+ * timeStampAuthority: true
44
+ * })
45
+ * ```
46
+ */
47
+ export class PdfEtsiCadesDetachedSignatureObject extends PdfSignatureObject {
48
+ /** Private key for signing. */
49
+ privateKey: ByteArray
50
+ /** Signer certificate. */
51
+ certificate: ByteArray
52
+ /** Additional certificates for chain building. */
53
+ additionalCertificates: ByteArray[]
54
+ /** Issuer certificate for OCSP requests. */
55
+ issuerCertificate?: ByteArray
56
+ /** Signing date. */
57
+ date?: Date
58
+ /** Reason for signing. */
59
+ reason?: string
60
+ /** Signing location. */
61
+ location?: string
62
+ /** Signature algorithm parameters. */
63
+ algorithm?: AsymmetricEncryptionAlgorithmParams
64
+ /** Revocation information or 'fetch' to retrieve automatically. */
65
+ revocationInfo?: RevocationInfo | 'fetch'
66
+ /** Timestamp authority configuration. */
67
+ timeStampAuthority?: TimeStampAuthority
68
+ /** Signature policy document reference. */
69
+ policyDocument?: SignaturePolicyDocument
70
+
71
+ /**
72
+ * Creates a new CAdES detached signature object.
73
+ *
74
+ * @param options - Signature configuration options.
75
+ */
76
+ constructor(
77
+ options: PdfSignatureSignOptions & {
78
+ privateKey: ByteArray
79
+ certificate: ByteArray
80
+ issuerCertificate?: ByteArray
81
+ additionalCertificates?: ByteArray[]
82
+ algorithm?: AsymmetricEncryptionAlgorithmParams
83
+ revocationInfo?: RevocationInfo | 'fetch'
84
+ timeStampAuthority?: TimeStampAuthority | true
85
+ policyDocument?: SignaturePolicyDocument
86
+ },
87
+ ) {
88
+ super(
89
+ new PdfSignatureDictionary({
90
+ Type: new PdfName('Sig'),
91
+ Filter: new PdfName('Adobe.PPKLite'),
92
+ SubFilter: new PdfName('ETSI.CAdES.detached'),
93
+ ContactInfo: options.contactInfo
94
+ ? new PdfString(options.contactInfo)
95
+ : undefined,
96
+ M: options.date ? new PdfDate(options.date) : undefined,
97
+ Name: options.name ? new PdfString(options.name) : undefined,
98
+ }),
99
+ )
100
+
101
+ this.privateKey = options.privateKey
102
+ this.certificate = options.certificate
103
+ this.issuerCertificate = options.issuerCertificate
104
+ this.additionalCertificates = options.additionalCertificates || []
105
+ this.date = options.date
106
+ this.reason = options.reason
107
+ this.location = options.location
108
+ this.algorithm = options.algorithm
109
+ this.revocationInfo = options.revocationInfo
110
+ this.timeStampAuthority =
111
+ options.timeStampAuthority === true
112
+ ? {
113
+ url: 'https://freetsa.org/tsr',
114
+ }
115
+ : options.timeStampAuthority
116
+ this.policyDocument = options.policyDocument
117
+ }
118
+
119
+ /**
120
+ * Signs the document bytes using CAdES detached format.
121
+ *
122
+ * @param options - Signing options with bytes and revocation embedding flag.
123
+ * @returns The CMS SignedData and revocation information.
124
+ */
125
+ sign: PdfSignatureObject['sign'] = async (options) => {
126
+ const { bytes } = options
127
+
128
+ const certificate: Certificate = Certificate.fromDer(this.certificate)
129
+ const additionalCertificates: Certificate[] =
130
+ this.additionalCertificates.map(Certificate.fromDer)
131
+
132
+ const signedAttributes = new SignerInfo.SignedAttributes()
133
+ const unsignedAttributes = new SignerInfo.UnsignedAttributes()
134
+
135
+ signedAttributes.push(
136
+ Attribute.signingCertificateV2(
137
+ await SigningCertificateV2.fromCertificates({
138
+ certificates: [certificate, ...additionalCertificates],
139
+ }),
140
+ ),
141
+ )
142
+
143
+ signedAttributes.push(Attribute.signingTime(this.date ?? new Date()))
144
+
145
+ if (this.reason) {
146
+ signedAttributes.push(
147
+ Attribute.commitmentTypeIndication(this.reason),
148
+ )
149
+ }
150
+
151
+ if (this.location) {
152
+ signedAttributes.push(
153
+ Attribute.signingLocation({
154
+ localityName: this.location,
155
+ }),
156
+ )
157
+ }
158
+
159
+ if (this.policyDocument) {
160
+ signedAttributes.push(
161
+ Attribute.signaturePolicyIdentifier(
162
+ new SignaturePolicyId({
163
+ sigPolicyId: this.policyDocument.oid,
164
+ sigPolicyHash: new OtherHashAlgAndValue({
165
+ hashAlgorithm:
166
+ DigestAlgorithmIdentifier.digestAlgorithm(
167
+ this.policyDocument.hashAlgorithm,
168
+ ),
169
+ hashValue: this.policyDocument.hash,
170
+ }),
171
+ sigPolicyQualifiers: [],
172
+ }),
173
+ ),
174
+ )
175
+ }
176
+
177
+ const revocationInfo =
178
+ this.revocationInfo === 'fetch'
179
+ ? await fetchRevocationInfo({
180
+ certificates: [
181
+ this.certificate,
182
+ ...(this.additionalCertificates ?? []),
183
+ ],
184
+ issuerCertificate: this.issuerCertificate,
185
+ })
186
+ : this.revocationInfo
187
+
188
+ if (options.embedRevocationInfo && revocationInfo) {
189
+ signedAttributes.push(
190
+ Attribute.adobeRevocationInfoArchival(
191
+ new RevocationInfoArchival({
192
+ crls: revocationInfo.crls?.map((x) =>
193
+ CertificateList.fromDer(x),
194
+ ),
195
+ ocsps: revocationInfo.ocsps?.map((x) =>
196
+ OCSPResponse.fromDer(x),
197
+ ),
198
+ otherRevInfo: revocationInfo.otherRevInfo?.map(
199
+ (x) =>
200
+ new OtherRevInfo({
201
+ type: x.type,
202
+ value: x.value,
203
+ }),
204
+ ),
205
+ }),
206
+ ),
207
+ )
208
+ }
209
+
210
+ const signedData = await SignedData.builder()
211
+ .addCertificate(...additionalCertificates)
212
+ .setData(bytes)
213
+ .setDetached(true)
214
+ .addSigner({
215
+ certificate,
216
+ privateKeyInfo: PrivateKeyInfo.fromDer(this.privateKey),
217
+ signedAttrs: signedAttributes,
218
+ unsignedAttrs: unsignedAttributes,
219
+ tsa: this.timeStampAuthority,
220
+ encryptionAlgorithm: this.algorithm,
221
+ })
222
+ .build()
223
+
224
+ return {
225
+ signedBytes: signedData.toCms().toDer(),
226
+ revocationInfo,
227
+ }
228
+ }
229
+ }
@@ -0,0 +1,92 @@
1
+ import { TimeStampAuthority } from '../types'
2
+ import { PdfSignatureObject, PdfSignatureSignOptions } from './base'
3
+ import { DigestAlgorithmIdentifier } from 'pki-lite/algorithms/AlgorithmIdentifier'
4
+ import { TimeStampReq } from 'pki-lite/timestamp/TimeStampReq'
5
+ import { MessageImprint } from 'pki-lite/timestamp/MessageImprint'
6
+ import { SignedData } from 'pki-lite/pkcs7/SignedData'
7
+ import { fetchRevocationInfo } from '../utils'
8
+
9
+ /**
10
+ * RFC 3161 timestamp signature object (ETSI.RFC3161).
11
+ * Creates document timestamps using a Time Stamp Authority (TSA).
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const timestamp = new PdfEtsiRfc3161SignatureObject({
16
+ * timeStampAuthority: { url: 'http://timestamp.example.com' }
17
+ * })
18
+ * ```
19
+ */
20
+ export class PdfEtsiRfc3161SignatureObject extends PdfSignatureObject {
21
+ /** Timestamp authority configuration. */
22
+ timeStampAuthority: TimeStampAuthority
23
+
24
+ /**
25
+ * Creates a new RFC 3161 timestamp signature object.
26
+ *
27
+ * @param options - Configuration including optional TSA settings.
28
+ */
29
+ constructor(
30
+ options: PdfSignatureSignOptions & {
31
+ timeStampAuthority?: TimeStampAuthority
32
+ name?: string
33
+ reason?: string
34
+ contactInfo?: string
35
+ location?: string
36
+ },
37
+ ) {
38
+ super({
39
+ ...options,
40
+ subfilter: 'ETSI.RFC3161',
41
+ })
42
+
43
+ this.timeStampAuthority = options.timeStampAuthority ?? {
44
+ url: 'https://freetsa.org/tsr',
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Creates a timestamp for the document bytes.
50
+ *
51
+ * @param options - Signing options with bytes to timestamp.
52
+ * @returns The timestamp token and revocation information.
53
+ * @throws Error if no timestamp token is received.
54
+ */
55
+ sign: PdfSignatureObject['sign'] = async (options) => {
56
+ const { bytes } = options
57
+
58
+ const digestAlgorithm =
59
+ DigestAlgorithmIdentifier.digestAlgorithm('SHA-512')
60
+
61
+ const timestampResponse = await TimeStampReq.create({
62
+ messageImprint: new MessageImprint({
63
+ hashAlgorithm: digestAlgorithm,
64
+ hashedMessage: await digestAlgorithm.digest(bytes),
65
+ }),
66
+ certReq: true,
67
+ }).request({
68
+ url: this.timeStampAuthority.url,
69
+ username: this.timeStampAuthority.username,
70
+ password: this.timeStampAuthority.password,
71
+ })
72
+
73
+ if (!timestampResponse.timeStampToken) {
74
+ throw new Error(
75
+ 'No timestamp token received. Response: ' +
76
+ timestampResponse.status,
77
+ )
78
+ }
79
+
80
+ const signatureBytes = timestampResponse.timeStampToken.toDer()
81
+
82
+ const signerCerts = SignedData.fromCms(signatureBytes).certificates
83
+ const revocationInfo = await fetchRevocationInfo({
84
+ certificates: signerCerts?.map((c) => c.toDer()) ?? [],
85
+ })
86
+
87
+ return {
88
+ signedBytes: signatureBytes,
89
+ revocationInfo,
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,6 @@
1
+ export * from './adbe-pkcs7-detached'
2
+ export * from './adbe-pkcs7-sha1'
3
+ export * from './adbe-x509-rsa-sha1'
4
+ export * from './etsi-cades-detached'
5
+ export * from './etsi-rfc3161'
6
+ export * from './base'