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.
- package/.commitlintrc.cjs +25 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/workflows/docs.yaml +93 -0
- package/.github/workflows/prepare-release.yaml +79 -0
- package/.github/workflows/release.yaml +80 -0
- package/.github/workflows/test.yaml +35 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +4 -0
- package/CONTRIBUTING.md +109 -0
- package/EXAMPLES.md +1515 -0
- package/LICENSE +21 -0
- package/README.md +285 -0
- package/examples/001-create-pdf.ts +112 -0
- package/examples/002-create-encrypted-pdf.ts +121 -0
- package/examples/003-sign-pdf.ts +347 -0
- package/examples/004-incremental-update.ts +206 -0
- package/examples/005-modify-acroform.ts +374 -0
- package/examples/006-tokeniser-example.ts +131 -0
- package/examples/007-decoder-example.ts +197 -0
- package/package.json +72 -0
- package/packages/pdf-lite/README.md +3 -0
- package/packages/pdf-lite/package.json +68 -0
- package/packages/pdf-lite/scripts/create-encryption-tests.sh +41 -0
- package/packages/pdf-lite/scripts/gen-signing-keys.sh +290 -0
- package/packages/pdf-lite/scripts/generate-all-signing-keys.sh +70 -0
- package/packages/pdf-lite/src/core/decoder.ts +454 -0
- package/packages/pdf-lite/src/core/generators.ts +128 -0
- package/packages/pdf-lite/src/core/incremental-parser.ts +221 -0
- package/packages/pdf-lite/src/core/index.ts +2 -0
- package/packages/pdf-lite/src/core/objects/pdf-array.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-boolean.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-comment.ts +50 -0
- package/packages/pdf-lite/src/core/objects/pdf-date.ts +74 -0
- package/packages/pdf-lite/src/core/objects/pdf-dictionary.ts +171 -0
- package/packages/pdf-lite/src/core/objects/pdf-hexadecimal.ts +54 -0
- package/packages/pdf-lite/src/core/objects/pdf-indirect-object.ts +137 -0
- package/packages/pdf-lite/src/core/objects/pdf-name.ts +19 -0
- package/packages/pdf-lite/src/core/objects/pdf-null.ts +15 -0
- package/packages/pdf-lite/src/core/objects/pdf-number.ts +98 -0
- package/packages/pdf-lite/src/core/objects/pdf-object-reference.ts +30 -0
- package/packages/pdf-lite/src/core/objects/pdf-object.ts +107 -0
- package/packages/pdf-lite/src/core/objects/pdf-start-xref.ts +39 -0
- package/packages/pdf-lite/src/core/objects/pdf-stream.ts +687 -0
- package/packages/pdf-lite/src/core/objects/pdf-string.ts +38 -0
- package/packages/pdf-lite/src/core/objects/pdf-trailer.ts +57 -0
- package/packages/pdf-lite/src/core/objects/pdf-xref-table.ts +264 -0
- package/packages/pdf-lite/src/core/parser.ts +22 -0
- package/packages/pdf-lite/src/core/ref.ts +102 -0
- package/packages/pdf-lite/src/core/serializer.ts +68 -0
- package/packages/pdf-lite/src/core/streams/object-stream.ts +20 -0
- package/packages/pdf-lite/src/core/tokeniser.ts +687 -0
- package/packages/pdf-lite/src/core/tokens/boolean-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/byte-offset-token.ts +20 -0
- package/packages/pdf-lite/src/core/tokens/comment-token.ts +32 -0
- package/packages/pdf-lite/src/core/tokens/end-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-object-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/end-stream-token.ts +11 -0
- package/packages/pdf-lite/src/core/tokens/hexadecimal-token.ts +22 -0
- package/packages/pdf-lite/src/core/tokens/name-token.ts +19 -0
- package/packages/pdf-lite/src/core/tokens/null-token.ts +9 -0
- package/packages/pdf-lite/src/core/tokens/number-token.ts +164 -0
- package/packages/pdf-lite/src/core/tokens/object-reference-token.ts +24 -0
- package/packages/pdf-lite/src/core/tokens/start-array-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-dictionary-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/start-object-token.ts +28 -0
- package/packages/pdf-lite/src/core/tokens/start-stream-token.ts +52 -0
- package/packages/pdf-lite/src/core/tokens/start-xref-token.ts +10 -0
- package/packages/pdf-lite/src/core/tokens/stream-chunk-token.ts +8 -0
- package/packages/pdf-lite/src/core/tokens/string-token.ts +17 -0
- package/packages/pdf-lite/src/core/tokens/token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/trailer-token.ts +12 -0
- package/packages/pdf-lite/src/core/tokens/whitespace-token.ts +43 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-entry-token.ts +65 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-section-start-token.ts +31 -0
- package/packages/pdf-lite/src/core/tokens/xref-table-start-token.ts +13 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes128.ts +63 -0
- package/packages/pdf-lite/src/crypto/ciphers/aes256.ts +50 -0
- package/packages/pdf-lite/src/crypto/ciphers/rc4.ts +82 -0
- package/packages/pdf-lite/src/crypto/constants.ts +10 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation-aes256.ts +213 -0
- package/packages/pdf-lite/src/crypto/key-derivation/key-derivation.ts +122 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-aes256.ts +79 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-128.ts +190 -0
- package/packages/pdf-lite/src/crypto/key-gen/key-gen-rc4-40.ts +129 -0
- package/packages/pdf-lite/src/crypto/types.ts +6 -0
- package/packages/pdf-lite/src/crypto/utils.ts +81 -0
- package/packages/pdf-lite/src/filters/ascii85.ts +128 -0
- package/packages/pdf-lite/src/filters/asciihex.ts +55 -0
- package/packages/pdf-lite/src/filters/flate.ts +39 -0
- package/packages/pdf-lite/src/filters/lzw.ts +144 -0
- package/packages/pdf-lite/src/filters/pass-through.ts +37 -0
- package/packages/pdf-lite/src/filters/runlength.ts +92 -0
- package/packages/pdf-lite/src/filters/types.ts +21 -0
- package/packages/pdf-lite/src/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/errors.ts +5 -0
- package/packages/pdf-lite/src/pdf/index.ts +4 -0
- package/packages/pdf-lite/src/pdf/pdf-document.ts +924 -0
- package/packages/pdf-lite/src/pdf/pdf-reader.ts +57 -0
- package/packages/pdf-lite/src/pdf/pdf-revision.ts +234 -0
- package/packages/pdf-lite/src/pdf/pdf-xref-lookup.ts +527 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv2.ts +58 -0
- package/packages/pdf-lite/src/security/crypt-filters/aesv3.ts +56 -0
- package/packages/pdf-lite/src/security/crypt-filters/base.ts +140 -0
- package/packages/pdf-lite/src/security/crypt-filters/identity.ts +40 -0
- package/packages/pdf-lite/src/security/crypt-filters/v2.ts +59 -0
- package/packages/pdf-lite/src/security/handlers/base.ts +625 -0
- package/packages/pdf-lite/src/security/handlers/pubSec.ts +413 -0
- package/packages/pdf-lite/src/security/handlers/utils.ts +304 -0
- package/packages/pdf-lite/src/security/handlers/v1.ts +225 -0
- package/packages/pdf-lite/src/security/handlers/v2.ts +128 -0
- package/packages/pdf-lite/src/security/handlers/v4.ts +379 -0
- package/packages/pdf-lite/src/security/handlers/v5.ts +298 -0
- package/packages/pdf-lite/src/security/types.ts +158 -0
- package/packages/pdf-lite/src/signing/document-security-store.ts +224 -0
- package/packages/pdf-lite/src/signing/index.ts +3 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-detached.ts +154 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-pkcs7-sha1.ts +161 -0
- package/packages/pdf-lite/src/signing/signatures/adbe-x509-rsa-sha1.ts +106 -0
- package/packages/pdf-lite/src/signing/signatures/base.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-cades-detached.ts +229 -0
- package/packages/pdf-lite/src/signing/signatures/etsi-rfc3161.ts +92 -0
- package/packages/pdf-lite/src/signing/signatures/index.ts +6 -0
- package/packages/pdf-lite/src/signing/signer.ts +120 -0
- package/packages/pdf-lite/src/signing/types.ts +86 -0
- package/packages/pdf-lite/src/signing/utils.ts +71 -0
- package/packages/pdf-lite/src/types.ts +44 -0
- package/packages/pdf-lite/src/utils/IterableReadableStream.ts +30 -0
- package/packages/pdf-lite/src/utils/algos.ts +446 -0
- package/packages/pdf-lite/src/utils/assert.ts +42 -0
- package/packages/pdf-lite/src/utils/bytesToHex.ts +18 -0
- package/packages/pdf-lite/src/utils/bytesToHexBytes.ts +27 -0
- package/packages/pdf-lite/src/utils/bytesToString.ts +17 -0
- package/packages/pdf-lite/src/utils/concatUint8Arrays.ts +26 -0
- package/packages/pdf-lite/src/utils/escapeString.ts +49 -0
- package/packages/pdf-lite/src/utils/hexBytesToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/hexBytesToString.ts +21 -0
- package/packages/pdf-lite/src/utils/hexToBytes.ts +18 -0
- package/packages/pdf-lite/src/utils/padBytes.ts +25 -0
- package/packages/pdf-lite/src/utils/predictors.ts +332 -0
- package/packages/pdf-lite/src/utils/replaceInBuffer.ts +56 -0
- package/packages/pdf-lite/src/utils/stringToBytes.ts +22 -0
- package/packages/pdf-lite/src/utils/stringToHexBytes.ts +23 -0
- package/packages/pdf-lite/src/utils/unescapeString.ts +123 -0
- package/packages/pdf-lite/test/acceptance/__snapshots__/versions.node.test.ts.snap +60766 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.3/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic-rc4-40.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.4/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.5/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.6/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/1.7/basic.pdf +0 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-aes-256.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-128.pdf +43 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic-rc4-40.pdf +44 -0
- package/packages/pdf-lite/test/acceptance/fixtures/2.0/basic.pdf +79 -0
- package/packages/pdf-lite/test/acceptance/versions.node.test.ts +41 -0
- package/packages/pdf-lite/test/unit/__snapshots__/decoder.node.test.ts.snap +86947 -0
- package/packages/pdf-lite/test/unit/__snapshots__/tokeniser.node.test.ts.snap +131829 -0
- package/packages/pdf-lite/test/unit/ciphers.test.ts +61 -0
- package/packages/pdf-lite/test/unit/decoder.node.test.ts +21 -0
- package/packages/pdf-lite/test/unit/decoder.test.ts +567 -0
- package/packages/pdf-lite/test/unit/filters.test.ts +67 -0
- package/packages/pdf-lite/test/unit/fixtures/basic.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-aes-256.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-128.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/encrypted_v1/basic-rc4-40.pdf +43 -0
- package/packages/pdf-lite/test/unit/fixtures/protectedAdobeLivecycle.pdf +0 -0
- package/packages/pdf-lite/test/unit/fixtures/rsa-2048/index.ts +187 -0
- package/packages/pdf-lite/test/unit/fixtures/template.pdf +0 -0
- package/packages/pdf-lite/test/unit/incremental-update.test.ts +0 -0
- package/packages/pdf-lite/test/unit/objects.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-document-signing.test.ts +0 -0
- package/packages/pdf-lite/test/unit/pdf-revision.test.ts +195 -0
- package/packages/pdf-lite/test/unit/pdf.browser.test.ts +0 -0
- package/packages/pdf-lite/test/unit/predictors.test.ts +226 -0
- package/packages/pdf-lite/test/unit/ref.test.ts +158 -0
- package/packages/pdf-lite/test/unit/security-handlers.test.ts +645 -0
- package/packages/pdf-lite/test/unit/serializer.test.ts +81 -0
- package/packages/pdf-lite/test/unit/signature-objects.test.ts +814 -0
- package/packages/pdf-lite/test/unit/string-escaping.test.ts +84 -0
- package/packages/pdf-lite/test/unit/tokeniser.node.test.ts +38 -0
- package/packages/pdf-lite/test/unit/tokeniser.test.ts +1213 -0
- package/packages/pdf-lite/test/unit/utils.test.ts +248 -0
- package/packages/pdf-lite/test/unit/xref-lookup.test.ts +72 -0
- package/packages/pdf-lite/tsconfig.json +4 -0
- package/packages/pdf-lite/tsconfig.prod.json +8 -0
- package/packages/pdf-lite/typedoc.json +14 -0
- package/packages/pdf-lite/vitest.config.ts +43 -0
- package/pnpm-workspace.yaml +2 -0
- package/renovate.json +34 -0
- package/scripts/build-examples.ts +30 -0
- package/scripts/bump-version.sh +56 -0
- package/scripts/gen-html-docs.sh +21 -0
- package/scripts/gen-md-docs.sh +15 -0
- package/scripts/prepare-release.sh +33 -0
- package/tsconfig.json +22 -0
- package/tsconfig.prod.json +12 -0
- package/typedoc.json +34 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { rsaSigningKeys } from './fixtures/rsa-2048'
|
|
3
|
+
import { PdfV1SecurityHandler } from '../../src/security/handlers/v1'
|
|
4
|
+
import { PdfV2SecurityHandler } from '../../src/security/handlers/v2'
|
|
5
|
+
import { PdfV4SecurityHandler } from '../../src/security/handlers/v4'
|
|
6
|
+
import { PdfV5SecurityHandler } from '../../src/security/handlers/v5'
|
|
7
|
+
import { PdfPublicKeySecurityHandler } from '../../src/security/handlers/pubSec'
|
|
8
|
+
import { PdfEncryptionDictionary } from '../../src/security/types'
|
|
9
|
+
import { createFromDictionary } from '../../src/security/handlers/utils'
|
|
10
|
+
import { PdfSecurityHandler } from '../../src/security/handlers/base'
|
|
11
|
+
import { AesV2CryptFilter } from '../../src/security/crypt-filters/aesv2'
|
|
12
|
+
import { PdfCryptFilter } from '../../src/security/crypt-filters/base'
|
|
13
|
+
import { V2CryptFilter } from '../../src/security/crypt-filters/v2'
|
|
14
|
+
import { AesV3CryptFilter } from '../../src/security/crypt-filters/aesv3'
|
|
15
|
+
import { PdfDictionary } from '../../src/core/objects/pdf-dictionary'
|
|
16
|
+
import { PdfName } from '../../src/core/objects/pdf-name'
|
|
17
|
+
import { PdfNumber } from '../../src/core/objects/pdf-number'
|
|
18
|
+
import { PdfHexadecimal } from '../../src/core/objects/pdf-hexadecimal'
|
|
19
|
+
import { PdfArray } from '../../src/core/objects/pdf-array'
|
|
20
|
+
import { ByteArray } from '../../src/types'
|
|
21
|
+
import { stringToBytes } from '../../src/utils/stringToBytes'
|
|
22
|
+
|
|
23
|
+
describe('Security Handlers', () => {
|
|
24
|
+
const handlerSettings: {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
handler: new (options: any) => PdfSecurityHandler
|
|
27
|
+
r: number
|
|
28
|
+
v: number
|
|
29
|
+
length: number
|
|
30
|
+
filter: `Standard` | `Adobe.PubSec`
|
|
31
|
+
recipients?: ByteArray[]
|
|
32
|
+
testCryptFilters?: boolean
|
|
33
|
+
}
|
|
34
|
+
} = {
|
|
35
|
+
'RC4-40': {
|
|
36
|
+
handler: PdfV1SecurityHandler,
|
|
37
|
+
r: 2,
|
|
38
|
+
v: 1,
|
|
39
|
+
length: 40,
|
|
40
|
+
filter: 'Standard',
|
|
41
|
+
},
|
|
42
|
+
'RC4-128': {
|
|
43
|
+
handler: PdfV2SecurityHandler,
|
|
44
|
+
r: 3,
|
|
45
|
+
v: 2,
|
|
46
|
+
length: 128,
|
|
47
|
+
filter: 'Standard',
|
|
48
|
+
},
|
|
49
|
+
'AES-128': {
|
|
50
|
+
handler: PdfV4SecurityHandler,
|
|
51
|
+
r: 4,
|
|
52
|
+
v: 4,
|
|
53
|
+
length: 128,
|
|
54
|
+
filter: 'Standard',
|
|
55
|
+
testCryptFilters: true,
|
|
56
|
+
},
|
|
57
|
+
'AES-256': {
|
|
58
|
+
handler: PdfV5SecurityHandler,
|
|
59
|
+
r: 5,
|
|
60
|
+
v: 5,
|
|
61
|
+
length: 256,
|
|
62
|
+
filter: 'Standard',
|
|
63
|
+
testCryptFilters: true,
|
|
64
|
+
},
|
|
65
|
+
PubSec: {
|
|
66
|
+
handler: PdfPublicKeySecurityHandler,
|
|
67
|
+
r: 5,
|
|
68
|
+
v: 5,
|
|
69
|
+
filter: 'Adobe.PubSec',
|
|
70
|
+
length: 256,
|
|
71
|
+
recipients: [new Uint8Array([])],
|
|
72
|
+
testCryptFilters: true,
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe('Create Handler from Dictionary', () => {
|
|
77
|
+
for (const [name, settings] of Object.entries(handlerSettings)) {
|
|
78
|
+
it(`${name}: should create handler from dictionary`, async () => {
|
|
79
|
+
const dict: PdfEncryptionDictionary = new PdfDictionary({
|
|
80
|
+
Filter: new PdfName(settings.filter),
|
|
81
|
+
V: new PdfNumber(settings.v),
|
|
82
|
+
R: new PdfNumber(settings.r),
|
|
83
|
+
O: new PdfHexadecimal(
|
|
84
|
+
new Uint8Array([
|
|
85
|
+
0x4c, 0x5c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e,
|
|
86
|
+
0x5f, 0x4b, 0x6c, 0x6f, 0x6d, 0x2c, 0x7a, 0x1c,
|
|
87
|
+
0x3f, 0x3d, 0x55, 0x4e, 0x5f, 0x4b, 0x6c, 0x6f,
|
|
88
|
+
0x6d, 0x2c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e,
|
|
89
|
+
0x5f, 0x4b, 0x6c, 0x6f, 0x6d, 0x2c,
|
|
90
|
+
]),
|
|
91
|
+
),
|
|
92
|
+
U: new PdfHexadecimal(
|
|
93
|
+
new Uint8Array([
|
|
94
|
+
0x28, 0x3c, 0x4e, 0x5f, 0x4b, 0x6c, 0x6f, 0x6d,
|
|
95
|
+
0x2c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e, 0x5f,
|
|
96
|
+
0x4b, 0x6c, 0x6f, 0x6d, 0x2c,
|
|
97
|
+
]),
|
|
98
|
+
),
|
|
99
|
+
UE: new PdfHexadecimal(
|
|
100
|
+
new Uint8Array([
|
|
101
|
+
0x28, 0x3c, 0x4e, 0x5f, 0x4b, 0x6c, 0x6f, 0x6d,
|
|
102
|
+
0x2c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e, 0x5f,
|
|
103
|
+
0x4b, 0x6c, 0x6f, 0x6d, 0x2c,
|
|
104
|
+
]),
|
|
105
|
+
),
|
|
106
|
+
OE: new PdfHexadecimal(
|
|
107
|
+
new Uint8Array([
|
|
108
|
+
0x28, 0x3c, 0x4e, 0x5f, 0x4b, 0x6c, 0x6f, 0x6d,
|
|
109
|
+
0x2c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e, 0x5f,
|
|
110
|
+
0x4b, 0x6c, 0x6f, 0x6d, 0x2c,
|
|
111
|
+
]),
|
|
112
|
+
),
|
|
113
|
+
Perms: new PdfHexadecimal(
|
|
114
|
+
new Uint8Array([
|
|
115
|
+
0x28, 0x3c, 0x4e, 0x5f, 0x4b, 0x6c, 0x6f, 0x6d,
|
|
116
|
+
0x2c, 0x7a, 0x1c, 0x3f, 0x3d, 0x55, 0x4e, 0x5f,
|
|
117
|
+
0x4b, 0x6c, 0x6f, 0x6d, 0x2c,
|
|
118
|
+
]),
|
|
119
|
+
),
|
|
120
|
+
P: new PdfNumber(-44),
|
|
121
|
+
Recipients: settings.recipients
|
|
122
|
+
? new PdfArray(
|
|
123
|
+
settings.recipients.map(
|
|
124
|
+
(r) => new PdfHexadecimal(r),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
: undefined,
|
|
128
|
+
Length: new PdfNumber(settings.length),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const handler = await createFromDictionary(dict, {
|
|
132
|
+
password: 'userpassword',
|
|
133
|
+
recipients:
|
|
134
|
+
settings.filter === 'Adobe.PubSec'
|
|
135
|
+
? [
|
|
136
|
+
{
|
|
137
|
+
certificate: rsaSigningKeys.cert,
|
|
138
|
+
},
|
|
139
|
+
]
|
|
140
|
+
: [],
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
expect(handler).toBeInstanceOf(settings.handler)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it(`${name}: should encrypt and decrypt data correctly`, async () => {
|
|
147
|
+
const data = stringToBytes('Hello, World!')
|
|
148
|
+
const objectNumber = 1
|
|
149
|
+
const generationNumber = 0
|
|
150
|
+
const password = 'userpassword'
|
|
151
|
+
|
|
152
|
+
const HandlerClass = settings.handler
|
|
153
|
+
const handler = new HandlerClass({
|
|
154
|
+
password,
|
|
155
|
+
documentId: 'test',
|
|
156
|
+
recipients:
|
|
157
|
+
settings.filter === 'Adobe.PubSec'
|
|
158
|
+
? [
|
|
159
|
+
{
|
|
160
|
+
certificate: rsaSigningKeys.cert,
|
|
161
|
+
},
|
|
162
|
+
]
|
|
163
|
+
: [],
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
await handler.write()
|
|
167
|
+
|
|
168
|
+
const dict = handler.dict
|
|
169
|
+
const parsedHandler = await createFromDictionary(dict, {
|
|
170
|
+
password,
|
|
171
|
+
recipients:
|
|
172
|
+
settings.filter === 'Adobe.PubSec'
|
|
173
|
+
? [
|
|
174
|
+
{
|
|
175
|
+
certificate: rsaSigningKeys.cert,
|
|
176
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
177
|
+
},
|
|
178
|
+
]
|
|
179
|
+
: [],
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(parsedHandler).toBeInstanceOf(settings.handler)
|
|
183
|
+
const encryptedData = await handler.encrypt(
|
|
184
|
+
'stream',
|
|
185
|
+
data,
|
|
186
|
+
objectNumber,
|
|
187
|
+
generationNumber,
|
|
188
|
+
)
|
|
189
|
+
const decryptedData = await parsedHandler.decrypt(
|
|
190
|
+
'stream',
|
|
191
|
+
encryptedData,
|
|
192
|
+
objectNumber,
|
|
193
|
+
generationNumber,
|
|
194
|
+
)
|
|
195
|
+
expect(new TextDecoder().decode(decryptedData)).toBe(
|
|
196
|
+
'Hello, World!',
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it(`${name}: should work with owner password`, async () => {
|
|
201
|
+
if (settings.filter === 'Adobe.PubSec') {
|
|
202
|
+
// Skip owner password test for PubSec handlers as they don't use owner passwords
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const data = stringToBytes('Hello, World!')
|
|
207
|
+
const objectNumber = 1
|
|
208
|
+
const generationNumber = 0
|
|
209
|
+
const userPassword = 'userpassword'
|
|
210
|
+
const ownerPassword = 'ownerpassword'
|
|
211
|
+
|
|
212
|
+
const HandlerClass = settings.handler
|
|
213
|
+
const handler = new HandlerClass({
|
|
214
|
+
password: userPassword,
|
|
215
|
+
ownerPassword: ownerPassword,
|
|
216
|
+
documentId: 'test',
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
await handler.write()
|
|
220
|
+
|
|
221
|
+
const dict = handler.dict
|
|
222
|
+
|
|
223
|
+
// Test that owner password can decrypt
|
|
224
|
+
const ownerHandler = await createFromDictionary(dict, {
|
|
225
|
+
ownerPassword: ownerPassword,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
expect(ownerHandler).toBeInstanceOf(settings.handler)
|
|
229
|
+
|
|
230
|
+
const encryptedData = await handler.encrypt(
|
|
231
|
+
'stream',
|
|
232
|
+
data,
|
|
233
|
+
objectNumber,
|
|
234
|
+
generationNumber,
|
|
235
|
+
)
|
|
236
|
+
const decryptedDataWithOwner = await ownerHandler.decrypt(
|
|
237
|
+
'stream',
|
|
238
|
+
encryptedData,
|
|
239
|
+
objectNumber,
|
|
240
|
+
generationNumber,
|
|
241
|
+
)
|
|
242
|
+
expect(new TextDecoder().decode(decryptedDataWithOwner)).toBe(
|
|
243
|
+
'Hello, World!',
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
// Test that user password can also decrypt
|
|
247
|
+
const userHandler = await createFromDictionary(dict, {
|
|
248
|
+
password: userPassword,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
expect(userHandler).toBeInstanceOf(settings.handler)
|
|
252
|
+
|
|
253
|
+
const decryptedDataWithUser = await userHandler.decrypt(
|
|
254
|
+
'stream',
|
|
255
|
+
encryptedData,
|
|
256
|
+
objectNumber,
|
|
257
|
+
generationNumber,
|
|
258
|
+
)
|
|
259
|
+
expect(new TextDecoder().decode(decryptedDataWithUser)).toBe(
|
|
260
|
+
'Hello, World!',
|
|
261
|
+
)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it(`${name}: should fail with incorrect password`, async () => {
|
|
265
|
+
if (settings.filter === 'Adobe.PubSec') {
|
|
266
|
+
// Skip password test for PubSec handlers as they use certificates
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const userPassword = 'userpassword'
|
|
271
|
+
const wrongPassword = 'wrongpassword'
|
|
272
|
+
|
|
273
|
+
const HandlerClass = settings.handler
|
|
274
|
+
const handler = new HandlerClass({
|
|
275
|
+
password: userPassword,
|
|
276
|
+
documentId: 'test',
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
await handler.write()
|
|
280
|
+
|
|
281
|
+
const dict = handler.dict
|
|
282
|
+
|
|
283
|
+
// Test that wrong password fails
|
|
284
|
+
await expect(
|
|
285
|
+
createFromDictionary(dict, {
|
|
286
|
+
password: wrongPassword,
|
|
287
|
+
}).computeObjectKey(),
|
|
288
|
+
).rejects.toThrow()
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
if (settings.testCryptFilters) {
|
|
292
|
+
it(`${name}: should support Identity crypt filter meaning no encryption`, async () => {
|
|
293
|
+
const userPassword = 'userpassword'
|
|
294
|
+
const handler = new handlerSettings[name].handler({
|
|
295
|
+
password: userPassword,
|
|
296
|
+
documentId: 'test',
|
|
297
|
+
recipients:
|
|
298
|
+
settings.filter === 'Adobe.PubSec'
|
|
299
|
+
? [
|
|
300
|
+
{
|
|
301
|
+
certificate: rsaSigningKeys.cert,
|
|
302
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
303
|
+
},
|
|
304
|
+
]
|
|
305
|
+
: [],
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
!(handler instanceof PdfV4SecurityHandler) &&
|
|
310
|
+
!(handler instanceof PdfPublicKeySecurityHandler)
|
|
311
|
+
) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
'Handler is not a PdfV4SecurityHandler or PdfPublicKeySecurityHandler',
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (handler instanceof PdfPublicKeySecurityHandler) {
|
|
318
|
+
const standardHandler =
|
|
319
|
+
handler.getStandardSecurityHandler()
|
|
320
|
+
if (
|
|
321
|
+
!(standardHandler instanceof PdfV4SecurityHandler)
|
|
322
|
+
) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
'Standard handler is not a PdfV4SecurityHandler',
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
standardHandler.setCryptFilterForType(
|
|
328
|
+
'stream',
|
|
329
|
+
'Identity',
|
|
330
|
+
)
|
|
331
|
+
} else {
|
|
332
|
+
handler.setCryptFilterForType('stream', 'Identity')
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const data = stringToBytes('Hello, World!')
|
|
336
|
+
const objectNumber = 1
|
|
337
|
+
const generationNumber = 0
|
|
338
|
+
|
|
339
|
+
await handler.write()
|
|
340
|
+
|
|
341
|
+
const dict = handler.dict
|
|
342
|
+
expect(
|
|
343
|
+
dict.values['CF']?.values['Identity'],
|
|
344
|
+
).not.toBeDefined() // Identity filter should not be listed in /CF
|
|
345
|
+
const parsedHandler = await createFromDictionary(dict, {
|
|
346
|
+
password: userPassword,
|
|
347
|
+
recipients:
|
|
348
|
+
settings.filter === 'Adobe.PubSec'
|
|
349
|
+
? [
|
|
350
|
+
{
|
|
351
|
+
certificate: rsaSigningKeys.cert,
|
|
352
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
353
|
+
},
|
|
354
|
+
]
|
|
355
|
+
: [],
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
expect(parsedHandler).toBeInstanceOf(settings.handler)
|
|
359
|
+
|
|
360
|
+
const encryptedData = await handler.encrypt(
|
|
361
|
+
'stream',
|
|
362
|
+
data,
|
|
363
|
+
objectNumber,
|
|
364
|
+
generationNumber,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
// Data should be unchanged as /Identity means no encryption
|
|
368
|
+
expect(encryptedData).toEqual(data)
|
|
369
|
+
|
|
370
|
+
const decryptedData = await parsedHandler.decrypt(
|
|
371
|
+
'stream',
|
|
372
|
+
encryptedData,
|
|
373
|
+
objectNumber,
|
|
374
|
+
generationNumber,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
expect(new TextDecoder().decode(decryptedData)).toBe(
|
|
378
|
+
'Hello, World!',
|
|
379
|
+
)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
const cryptFilters: PdfCryptFilter[] = [
|
|
383
|
+
new V2CryptFilter({ authEvent: 'DocOpen' }),
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
if (name !== 'RC4-40') {
|
|
387
|
+
cryptFilters.push(
|
|
388
|
+
new AesV2CryptFilter({ authEvent: 'DocOpen' }),
|
|
389
|
+
)
|
|
390
|
+
cryptFilters.push(
|
|
391
|
+
new AesV3CryptFilter({ authEvent: 'EFOpen' }),
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
for (const cf of cryptFilters) {
|
|
396
|
+
it(`${name}: should support ${cf.constructor.name} crypt filter`, async () => {
|
|
397
|
+
const userPassword = 'userpassword'
|
|
398
|
+
const handler = new handlerSettings[name].handler({
|
|
399
|
+
password: userPassword,
|
|
400
|
+
documentId: 'test',
|
|
401
|
+
recipients:
|
|
402
|
+
settings.filter === 'Adobe.PubSec'
|
|
403
|
+
? [
|
|
404
|
+
{
|
|
405
|
+
certificate: rsaSigningKeys.cert,
|
|
406
|
+
privateKey:
|
|
407
|
+
rsaSigningKeys.privateKey,
|
|
408
|
+
},
|
|
409
|
+
]
|
|
410
|
+
: [],
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
if (
|
|
414
|
+
!(handler instanceof PdfV4SecurityHandler) &&
|
|
415
|
+
!(handler instanceof PdfPublicKeySecurityHandler)
|
|
416
|
+
) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
'Handler is not a PdfV4SecurityHandler or PdfPublicKeySecurityHandler',
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
cf.encrypt = vi.fn(cf.encrypt)
|
|
423
|
+
cf.decrypt = vi.fn(cf.decrypt)
|
|
424
|
+
|
|
425
|
+
if (handler instanceof PdfPublicKeySecurityHandler) {
|
|
426
|
+
const standardHandler =
|
|
427
|
+
handler.getStandardSecurityHandler()
|
|
428
|
+
if (
|
|
429
|
+
!(
|
|
430
|
+
standardHandler instanceof
|
|
431
|
+
PdfV4SecurityHandler
|
|
432
|
+
)
|
|
433
|
+
) {
|
|
434
|
+
throw new Error(
|
|
435
|
+
'Standard handler is not a PdfV4SecurityHandler',
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
standardHandler.setCryptFilter('StdCF', cf)
|
|
439
|
+
standardHandler.setCryptFilterForType(
|
|
440
|
+
'stream',
|
|
441
|
+
'StdCF',
|
|
442
|
+
)
|
|
443
|
+
} else {
|
|
444
|
+
handler.setCryptFilter('StdCF', cf)
|
|
445
|
+
handler.setCryptFilterForType('stream', 'StdCF')
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const data = stringToBytes('Hello, World!')
|
|
449
|
+
const objectNumber = 1
|
|
450
|
+
const generationNumber = 0
|
|
451
|
+
|
|
452
|
+
await handler.write()
|
|
453
|
+
const dict = handler.dict
|
|
454
|
+
const parsedHandler = await createFromDictionary(dict, {
|
|
455
|
+
password: userPassword,
|
|
456
|
+
recipients:
|
|
457
|
+
settings.filter === 'Adobe.PubSec'
|
|
458
|
+
? [
|
|
459
|
+
{
|
|
460
|
+
certificate: rsaSigningKeys.cert,
|
|
461
|
+
privateKey:
|
|
462
|
+
rsaSigningKeys.privateKey,
|
|
463
|
+
},
|
|
464
|
+
]
|
|
465
|
+
: [],
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
expect(parsedHandler).toBeInstanceOf(settings.handler)
|
|
469
|
+
|
|
470
|
+
const encryptedData = await handler.encrypt(
|
|
471
|
+
'stream',
|
|
472
|
+
data,
|
|
473
|
+
objectNumber,
|
|
474
|
+
generationNumber,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
const decryptedData = await parsedHandler.decrypt(
|
|
478
|
+
'stream',
|
|
479
|
+
encryptedData,
|
|
480
|
+
objectNumber,
|
|
481
|
+
generationNumber,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
expect(new TextDecoder().decode(decryptedData)).toBe(
|
|
485
|
+
'Hello, World!',
|
|
486
|
+
)
|
|
487
|
+
expect(cf.encrypt).toHaveBeenCalled()
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
describe('Owner Password Logic', () => {
|
|
495
|
+
it('should derive user password from owner password correctly', async () => {
|
|
496
|
+
const userPassword = 'user123'
|
|
497
|
+
const ownerPassword = 'owner456'
|
|
498
|
+
|
|
499
|
+
// Test with V2 handler (RC4-128)
|
|
500
|
+
const handler = new PdfV2SecurityHandler({
|
|
501
|
+
password: userPassword,
|
|
502
|
+
ownerPassword: ownerPassword,
|
|
503
|
+
documentId: 'test',
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
await handler.write()
|
|
507
|
+
const dict = handler.dict
|
|
508
|
+
|
|
509
|
+
// Create handler using owner password - it should be able to derive the user password
|
|
510
|
+
const ownerHandler = await createFromDictionary(dict, {
|
|
511
|
+
ownerPassword: ownerPassword,
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
expect(ownerHandler).toBeInstanceOf(PdfV2SecurityHandler)
|
|
515
|
+
|
|
516
|
+
// Test encryption/decryption works
|
|
517
|
+
const data = stringToBytes('Test data')
|
|
518
|
+
const encryptedData = await handler.encrypt('stream', data, 1, 0)
|
|
519
|
+
const decryptedData = await ownerHandler.decrypt(
|
|
520
|
+
'stream',
|
|
521
|
+
encryptedData,
|
|
522
|
+
1,
|
|
523
|
+
0,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
expect(new TextDecoder().decode(decryptedData)).toBe('Test data')
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
it('should handle owner password same as user password', async () => {
|
|
530
|
+
const password = 'samepassword'
|
|
531
|
+
|
|
532
|
+
// Test when owner password is the same as user password
|
|
533
|
+
const handler = new PdfV2SecurityHandler({
|
|
534
|
+
password: password,
|
|
535
|
+
ownerPassword: password,
|
|
536
|
+
documentId: 'test',
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
await handler.write()
|
|
540
|
+
const dict = handler.dict
|
|
541
|
+
|
|
542
|
+
// Should work with the same password
|
|
543
|
+
const samePasswordHandler = await createFromDictionary(dict, {
|
|
544
|
+
password: password,
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
expect(samePasswordHandler).toBeInstanceOf(PdfV2SecurityHandler)
|
|
548
|
+
|
|
549
|
+
// Test encryption/decryption works
|
|
550
|
+
const data = stringToBytes('Same password test')
|
|
551
|
+
const encryptedData = await handler.encrypt('stream', data, 1, 0)
|
|
552
|
+
const decryptedData = await samePasswordHandler.decrypt(
|
|
553
|
+
'stream',
|
|
554
|
+
encryptedData,
|
|
555
|
+
1,
|
|
556
|
+
0,
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
expect(new TextDecoder().decode(decryptedData)).toBe(
|
|
560
|
+
'Same password test',
|
|
561
|
+
)
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
it('should handle empty owner password (defaults to user password)', async () => {
|
|
565
|
+
const userPassword = 'userpass'
|
|
566
|
+
|
|
567
|
+
// Test when no owner password is provided (should default to user password)
|
|
568
|
+
const handler = new PdfV2SecurityHandler({
|
|
569
|
+
password: userPassword,
|
|
570
|
+
documentId: 'test',
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
await handler.write()
|
|
574
|
+
const dict = handler.dict
|
|
575
|
+
|
|
576
|
+
// Should work with user password as owner password
|
|
577
|
+
const userAsOwnerHandler = await createFromDictionary(dict, {
|
|
578
|
+
password: userPassword,
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
expect(userAsOwnerHandler).toBeInstanceOf(PdfV2SecurityHandler)
|
|
582
|
+
|
|
583
|
+
// Test encryption/decryption works
|
|
584
|
+
const data = stringToBytes('Default owner password test')
|
|
585
|
+
const encryptedData = await handler.encrypt('stream', data, 1, 0)
|
|
586
|
+
const decryptedData = await userAsOwnerHandler.decrypt(
|
|
587
|
+
'stream',
|
|
588
|
+
encryptedData,
|
|
589
|
+
1,
|
|
590
|
+
0,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
expect(new TextDecoder().decode(decryptedData)).toBe(
|
|
594
|
+
'Default owner password test',
|
|
595
|
+
)
|
|
596
|
+
})
|
|
597
|
+
})
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
describe('PubSec Security Handler', () => {
|
|
601
|
+
it('should encrypt and decrypt data correctly', async () => {
|
|
602
|
+
const standardHandler = new PdfV4SecurityHandler({
|
|
603
|
+
documentId: 'test',
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
const encryptionHandler = new PdfPublicKeySecurityHandler({
|
|
607
|
+
standardSecurityHandler: standardHandler,
|
|
608
|
+
recipients: [
|
|
609
|
+
{
|
|
610
|
+
certificate: rsaSigningKeys.cert,
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
const decryptionHandler = new PdfPublicKeySecurityHandler({
|
|
616
|
+
standardSecurityHandler: standardHandler,
|
|
617
|
+
recipients: [
|
|
618
|
+
{
|
|
619
|
+
certificate: rsaSigningKeys.cert,
|
|
620
|
+
privateKey: rsaSigningKeys.privateKey,
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
await encryptionHandler.write()
|
|
626
|
+
const dict = encryptionHandler.dict
|
|
627
|
+
decryptionHandler.readEncryptionDictionary(dict)
|
|
628
|
+
|
|
629
|
+
const data = stringToBytes('Hello, World!')
|
|
630
|
+
const encryptedData = await encryptionHandler.encrypt(
|
|
631
|
+
'stream',
|
|
632
|
+
data,
|
|
633
|
+
1,
|
|
634
|
+
0,
|
|
635
|
+
)
|
|
636
|
+
const decryptedData = await decryptionHandler.decrypt(
|
|
637
|
+
'stream',
|
|
638
|
+
encryptedData,
|
|
639
|
+
1,
|
|
640
|
+
0,
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
expect(new TextDecoder().decode(decryptedData)).toBe('Hello, World!')
|
|
644
|
+
})
|
|
645
|
+
})
|