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,225 @@
|
|
|
1
|
+
import { rc4 } from '../../crypto/ciphers/rc4'
|
|
2
|
+
import {
|
|
3
|
+
computeMasterKey,
|
|
4
|
+
deriveObjectKey,
|
|
5
|
+
} from '../../crypto/key-derivation/key-derivation'
|
|
6
|
+
import {
|
|
7
|
+
computeORc4_40,
|
|
8
|
+
computeURc4_40,
|
|
9
|
+
decryptUserPasswordRc4_40,
|
|
10
|
+
} from '../../crypto/key-gen/key-gen-rc4-40'
|
|
11
|
+
import { Cipher } from '../../crypto/types'
|
|
12
|
+
import { ByteArray } from '../../types'
|
|
13
|
+
import { assert } from '../../utils/assert'
|
|
14
|
+
import { bytesToString } from '../../utils/bytesToString'
|
|
15
|
+
import { stringToBytes } from '../../utils/stringToBytes'
|
|
16
|
+
import { PdfEncryptionAlgorithmType } from '../types'
|
|
17
|
+
import { PdfStandardSecurityHandler } from './base'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* V1 security handler implementing 40-bit RC4 encryption.
|
|
21
|
+
* This is the original PDF encryption format (PDF 1.1).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const handler = new PdfV1SecurityHandler({
|
|
26
|
+
* password: 'user123',
|
|
27
|
+
* ownerPassword: 'admin456'
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class PdfV1SecurityHandler extends PdfStandardSecurityHandler {
|
|
32
|
+
/**
|
|
33
|
+
* Gets the encryption revision number.
|
|
34
|
+
*
|
|
35
|
+
* @returns 2 for V1 encryption.
|
|
36
|
+
*/
|
|
37
|
+
getRevision(): number {
|
|
38
|
+
return 2
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets the encryption version number.
|
|
43
|
+
*
|
|
44
|
+
* @returns 1 for 40-bit RC4 encryption.
|
|
45
|
+
*/
|
|
46
|
+
getVersion(): number {
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Gets the encryption key length in bits.
|
|
52
|
+
*
|
|
53
|
+
* @returns 40 for V1 encryption.
|
|
54
|
+
*/
|
|
55
|
+
getKeyBits(): number {
|
|
56
|
+
return 40
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Computes the owner key (O value) using RC4-40 algorithm.
|
|
61
|
+
*
|
|
62
|
+
* @returns The computed owner key.
|
|
63
|
+
*/
|
|
64
|
+
protected async computeOwnerKey(): Promise<ByteArray> {
|
|
65
|
+
return await computeORc4_40(
|
|
66
|
+
this.ownerPassword ?? this.password,
|
|
67
|
+
this.password,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Computes the user key (U value) using RC4-40 algorithm.
|
|
73
|
+
*
|
|
74
|
+
* @returns The computed user key.
|
|
75
|
+
* @throws Error if document ID, owner key, or permissions are not set.
|
|
76
|
+
*/
|
|
77
|
+
protected async computeUserKey(): Promise<ByteArray> {
|
|
78
|
+
if (!this.documentId) {
|
|
79
|
+
throw new Error('Document ID is required to compute U value')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!this.ownerKey) {
|
|
83
|
+
throw new Error('Owner key is required to compute U value')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof this.permissions !== 'number') {
|
|
87
|
+
throw new Error('Permissions are required to compute U value')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return await computeURc4_40(
|
|
91
|
+
this.password,
|
|
92
|
+
this.ownerKey,
|
|
93
|
+
this.permissions,
|
|
94
|
+
this.documentId.items[0].bytes,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Computes the master encryption key from the password.
|
|
100
|
+
*
|
|
101
|
+
* @returns The computed master key.
|
|
102
|
+
* @throws Error if required parameters are missing or password is incorrect.
|
|
103
|
+
*/
|
|
104
|
+
protected async computeMasterKey(): Promise<ByteArray> {
|
|
105
|
+
await this.initKeys()
|
|
106
|
+
|
|
107
|
+
assert(this.ownerKey, 'ownerKey is required')
|
|
108
|
+
assert(this.permissions !== undefined, 'Permissions are required')
|
|
109
|
+
assert(this.documentId, 'Document ID is required')
|
|
110
|
+
assert(
|
|
111
|
+
this.encryptMetadata !== undefined,
|
|
112
|
+
'Encrypt metadata is required',
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if (this.ownerPassword) {
|
|
116
|
+
const recoverPass = await this.recoverUserPassword(
|
|
117
|
+
this.ownerPassword,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return await computeMasterKey(
|
|
121
|
+
stringToBytes(recoverPass),
|
|
122
|
+
this.ownerKey,
|
|
123
|
+
this.permissions,
|
|
124
|
+
this.documentId.items[0].bytes,
|
|
125
|
+
this.getKeyBits(),
|
|
126
|
+
this.encryptMetadata,
|
|
127
|
+
this.getRevision(),
|
|
128
|
+
)
|
|
129
|
+
} else {
|
|
130
|
+
const expectedUValue = await this.computeUserKey()
|
|
131
|
+
|
|
132
|
+
assert(
|
|
133
|
+
this.userKey &&
|
|
134
|
+
expectedUValue.length === this.userKey.length &&
|
|
135
|
+
expectedUValue.every(
|
|
136
|
+
(byte, index) => byte === this.userKey![index],
|
|
137
|
+
),
|
|
138
|
+
'Incorrect user password',
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return await computeMasterKey(
|
|
143
|
+
this.password,
|
|
144
|
+
this.ownerKey,
|
|
145
|
+
this.permissions,
|
|
146
|
+
this.documentId.items[0].bytes,
|
|
147
|
+
this.getKeyBits(),
|
|
148
|
+
this.encryptMetadata,
|
|
149
|
+
this.getRevision(),
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Computes the object-specific encryption key.
|
|
155
|
+
*
|
|
156
|
+
* @param objectNumber - The PDF object number.
|
|
157
|
+
* @param generationNumber - The PDF generation number.
|
|
158
|
+
* @param algorithm - Optional algorithm type for key derivation.
|
|
159
|
+
* @returns The computed object key.
|
|
160
|
+
* @throws Error if object or generation number is invalid.
|
|
161
|
+
*/
|
|
162
|
+
async computeObjectKey(
|
|
163
|
+
objectNumber?: number,
|
|
164
|
+
generationNumber?: number,
|
|
165
|
+
algorithm?: PdfEncryptionAlgorithmType,
|
|
166
|
+
): Promise<ByteArray> {
|
|
167
|
+
assert(
|
|
168
|
+
objectNumber !== undefined,
|
|
169
|
+
'Object number is required to derive the key',
|
|
170
|
+
)
|
|
171
|
+
assert(
|
|
172
|
+
generationNumber !== undefined,
|
|
173
|
+
'Generation number is required to derive the key',
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
assert(objectNumber > 0, 'Object number cannot be zero or negative')
|
|
177
|
+
assert(generationNumber >= 0, 'Generation number cannot be negative')
|
|
178
|
+
|
|
179
|
+
this.masterKey ||= await this.computeMasterKey()
|
|
180
|
+
|
|
181
|
+
const key = await deriveObjectKey(
|
|
182
|
+
this.masterKey,
|
|
183
|
+
objectNumber,
|
|
184
|
+
generationNumber,
|
|
185
|
+
algorithm === 'AES-128-CBC',
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return key
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Gets an RC4 cipher for the specified object.
|
|
193
|
+
*
|
|
194
|
+
* @param objectNumber - The PDF object number.
|
|
195
|
+
* @param generationNumber - The PDF generation number.
|
|
196
|
+
* @returns An RC4 cipher instance.
|
|
197
|
+
*/
|
|
198
|
+
protected async getCipher(
|
|
199
|
+
objectNumber?: number,
|
|
200
|
+
generationNumber?: number,
|
|
201
|
+
): Promise<Cipher> {
|
|
202
|
+
const key = await this.computeObjectKey(objectNumber, generationNumber)
|
|
203
|
+
|
|
204
|
+
return rc4(key)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Recovers the user password from the owner password.
|
|
209
|
+
*
|
|
210
|
+
* @param ownerPassword - The owner password.
|
|
211
|
+
* @returns The recovered user password as a string.
|
|
212
|
+
*/
|
|
213
|
+
async recoverUserPassword(
|
|
214
|
+
ownerPassword?: ByteArray | string,
|
|
215
|
+
): Promise<string> {
|
|
216
|
+
ownerPassword ||= this.ownerPassword
|
|
217
|
+
|
|
218
|
+
const password = await decryptUserPasswordRc4_40(
|
|
219
|
+
stringToBytes(ownerPassword!),
|
|
220
|
+
this.ownerKey!,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return bytesToString(password)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { rc4 } from '../../crypto/ciphers/rc4'
|
|
2
|
+
import {
|
|
3
|
+
computeOValueRc4_128,
|
|
4
|
+
computeUValueRc4_128,
|
|
5
|
+
decryptUserPasswordRc4_128,
|
|
6
|
+
} from '../../crypto/key-gen/key-gen-rc4-128'
|
|
7
|
+
import { Cipher } from '../../crypto/types'
|
|
8
|
+
import { ByteArray } from '../../types'
|
|
9
|
+
import { bytesToString } from '../../utils/bytesToString'
|
|
10
|
+
import { stringToBytes } from '../../utils/stringToBytes'
|
|
11
|
+
import { PdfV1SecurityHandler } from './v1'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* V2 security handler implementing 128-bit RC4 encryption.
|
|
15
|
+
* Extends V1 with stronger key length (PDF 1.4).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const handler = new PdfV2SecurityHandler({
|
|
20
|
+
* password: 'user123',
|
|
21
|
+
* ownerPassword: 'admin456'
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export class PdfV2SecurityHandler extends PdfV1SecurityHandler {
|
|
26
|
+
/**
|
|
27
|
+
* Gets the encryption revision number.
|
|
28
|
+
*
|
|
29
|
+
* @returns 3 for V2 encryption.
|
|
30
|
+
*/
|
|
31
|
+
getRevision(): number {
|
|
32
|
+
return 3
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Gets the encryption version number.
|
|
37
|
+
*
|
|
38
|
+
* @returns 2 for 128-bit RC4 encryption.
|
|
39
|
+
*/
|
|
40
|
+
getVersion(): number {
|
|
41
|
+
return 2
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Gets the encryption key length in bits.
|
|
46
|
+
*
|
|
47
|
+
* @returns 128 for V2 encryption.
|
|
48
|
+
*/
|
|
49
|
+
getKeyBits(): number {
|
|
50
|
+
return 128
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Computes the owner key (O value) using RC4-128 algorithm.
|
|
55
|
+
*
|
|
56
|
+
* @returns The computed owner key.
|
|
57
|
+
*/
|
|
58
|
+
protected async computeOwnerKey(): Promise<ByteArray> {
|
|
59
|
+
return await computeOValueRc4_128(
|
|
60
|
+
this.ownerPassword ?? this.password,
|
|
61
|
+
this.password,
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Computes the user key (U value) using RC4-128 algorithm.
|
|
67
|
+
*
|
|
68
|
+
* @returns The computed user key.
|
|
69
|
+
* @throws Error if document ID, owner key, or permissions are not set.
|
|
70
|
+
*/
|
|
71
|
+
protected async computeUserKey(): Promise<ByteArray> {
|
|
72
|
+
if (!this.documentId) {
|
|
73
|
+
throw new Error('Document ID is required to compute U value')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!this.ownerKey) {
|
|
77
|
+
throw new Error('Owner key is required to compute U value')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof this.permissions !== 'number') {
|
|
81
|
+
throw new Error('Permissions are required to compute U value')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return await computeUValueRc4_128(
|
|
85
|
+
this.password,
|
|
86
|
+
this.ownerKey,
|
|
87
|
+
this.permissions,
|
|
88
|
+
this.documentId.items[0].bytes,
|
|
89
|
+
this.encryptMetadata,
|
|
90
|
+
this.getRevision(),
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets an RC4 cipher for the specified object.
|
|
96
|
+
*
|
|
97
|
+
* @param objectNumber - The PDF object number.
|
|
98
|
+
* @param generationNumber - The PDF generation number.
|
|
99
|
+
* @returns An RC4 cipher instance.
|
|
100
|
+
*/
|
|
101
|
+
protected async getCipher(
|
|
102
|
+
objectNumber?: number,
|
|
103
|
+
generationNumber?: number,
|
|
104
|
+
): Promise<Cipher> {
|
|
105
|
+
const key = await this.computeObjectKey(objectNumber, generationNumber)
|
|
106
|
+
|
|
107
|
+
return rc4(key)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Recovers the user password from the owner password.
|
|
112
|
+
*
|
|
113
|
+
* @param ownerPassword - The owner password.
|
|
114
|
+
* @returns The recovered user password as a string.
|
|
115
|
+
*/
|
|
116
|
+
async recoverUserPassword(
|
|
117
|
+
ownerPassword?: ByteArray | string,
|
|
118
|
+
): Promise<string> {
|
|
119
|
+
ownerPassword ||= this.ownerPassword
|
|
120
|
+
|
|
121
|
+
const password = await decryptUserPasswordRc4_128(
|
|
122
|
+
stringToBytes(ownerPassword!),
|
|
123
|
+
this.ownerKey!,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return bytesToString(password)
|
|
127
|
+
}
|
|
128
|
+
}
|