pdf-parser.js 5.2.26 → 5.2.28
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/dist/{afm-widths-BYiX2L9D.cjs → afm-widths-BHD2HZIL.cjs} +553 -642
- package/dist/{afm-widths-blUDDjlI.js → afm-widths-B_IGbGky.js} +553 -642
- package/dist/afm-widths.cjs +1 -1
- package/dist/afm-widths.js +1 -1
- package/dist/annotations.cjs +5 -3
- package/dist/annotations.d.cts +2 -2
- package/dist/annotations.d.ts +2 -2
- package/dist/annotations.js +5 -3
- package/dist/attachments.d.cts +2 -2
- package/dist/attachments.d.ts +2 -2
- package/dist/builtin-encoding.cjs +17 -6
- package/dist/builtin-encoding.js +17 -6
- package/dist/bytes/crc32.cjs +5 -3
- package/dist/bytes/crc32.js +5 -3
- package/dist/bytes/flate.cjs +0 -1
- package/dist/bytes/flate.js +0 -1
- package/dist/cff-bounds.cjs +73 -45
- package/dist/cff-bounds.js +73 -45
- package/dist/cff.cjs +13 -6
- package/dist/cff.js +13 -6
- package/dist/cmap-table.cjs +54 -26
- package/dist/cmap-table.js +54 -26
- package/dist/cmap.cjs +5 -3
- package/dist/cmap.js +5 -3
- package/dist/content-read.d.cts +1 -1
- package/dist/content-read.d.ts +1 -1
- package/dist/content-write.cjs +21 -14
- package/dist/content-write.d.cts +2 -2
- package/dist/content-write.d.ts +2 -2
- package/dist/content-write.js +21 -14
- package/dist/crypto/aes.cjs +76 -55
- package/dist/crypto/aes.js +76 -55
- package/dist/crypto/md5.cjs +345 -149
- package/dist/crypto/md5.js +345 -149
- package/dist/crypto/rc4.cjs +5 -4
- package/dist/crypto/rc4.js +5 -4
- package/dist/crypto/sha2.cjs +92 -43
- package/dist/crypto/sha2.js +92 -43
- package/dist/document.cjs +17 -11
- package/dist/document.d.cts +1 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.js +17 -11
- package/dist/{embedded-font-Bnhh4juV.d.ts → embedded-font-ByD7ZiQS.d.ts} +1 -1
- package/dist/{embedded-font-CEf-zBCA.d.cts → embedded-font-DgVBedw6.d.cts} +1 -1
- package/dist/embedded-font-write.d.cts +2 -2
- package/dist/embedded-font-write.d.ts +2 -2
- package/dist/embedded-font.cjs +4 -2
- package/dist/embedded-font.d.cts +1 -1
- package/dist/embedded-font.d.ts +1 -1
- package/dist/embedded-font.js +4 -2
- package/dist/encoding.cjs +1 -1
- package/dist/encoding.js +1 -1
- package/dist/encrypt-write.cjs +25 -21
- package/dist/encrypt-write.d.cts +1 -1
- package/dist/encrypt-write.d.ts +1 -1
- package/dist/encrypt-write.js +25 -21
- package/dist/encrypt.cjs +43 -64
- package/dist/encrypt.d.cts +1 -1
- package/dist/encrypt.d.ts +1 -1
- package/dist/encrypt.js +43 -64
- package/dist/filters.cjs +46 -26
- package/dist/filters.d.cts +1 -1
- package/dist/filters.d.ts +1 -1
- package/dist/filters.js +46 -26
- package/dist/font-face.cjs +2 -1
- package/dist/font-face.js +2 -1
- package/dist/font-read.cjs +12 -6
- package/dist/font-read.d.cts +1 -1
- package/dist/font-read.d.ts +1 -1
- package/dist/font-read.js +12 -6
- package/dist/font-registry.d.cts +3 -3
- package/dist/font-registry.d.ts +3 -3
- package/dist/font-tables.cjs +55 -28
- package/dist/font-tables.js +55 -28
- package/dist/form.cjs +13 -8
- package/dist/form.d.cts +2 -2
- package/dist/form.d.ts +2 -2
- package/dist/form.js +13 -8
- package/dist/gdef-table.cjs +6 -3
- package/dist/gdef-table.js +6 -3
- package/dist/{glyf-contours-C0epBNqP.d.ts → glyf-contours-B1PsI9oA.d.ts} +1 -1
- package/dist/{glyf-contours-BRpvd0rt.d.cts → glyf-contours-NHBpgks-.d.cts} +1 -1
- package/dist/glyf-contours.cjs +3 -1
- package/dist/glyf-contours.d.cts +1 -1
- package/dist/glyf-contours.d.ts +1 -1
- package/dist/glyf-contours.js +3 -1
- package/dist/glyf.cjs +22 -11
- package/dist/glyf.js +22 -11
- package/dist/gpos-table.cjs +37 -19
- package/dist/gpos-table.js +37 -19
- package/dist/gsub-table.cjs +40 -25
- package/dist/gsub-table.js +40 -25
- package/dist/hmtx-table.cjs +5 -3
- package/dist/hmtx-table.js +5 -3
- package/dist/image/ccitt-encode.cjs +8 -5
- package/dist/image/ccitt-encode.js +8 -5
- package/dist/image/ccitt.cjs +801 -209
- package/dist/image/ccitt.js +801 -209
- package/dist/image/jbig2-arith.cjs +340 -306
- package/dist/image/jbig2-arith.js +340 -306
- package/dist/image/jbig2-bitmap.cjs +8 -5
- package/dist/image/jbig2-bitmap.js +8 -5
- package/dist/image/jbig2-generic.cjs +240 -69
- package/dist/image/jbig2-generic.js +240 -69
- package/dist/image/jbig2-text.cjs +4 -2
- package/dist/image/jbig2-text.js +4 -2
- package/dist/image/jbig2.cjs +69 -31
- package/dist/image/jbig2.js +69 -31
- package/dist/image/jp2-boxes.cjs +52 -27
- package/dist/image/jp2-boxes.js +52 -27
- package/dist/image/jpeg-info.cjs +29 -24
- package/dist/image/jpeg-info.js +29 -24
- package/dist/image/jpeg2000-codestream.cjs +61 -32
- package/dist/image/jpeg2000-codestream.js +61 -32
- package/dist/image/jpeg2000-dwt.cjs +2 -1
- package/dist/image/jpeg2000-dwt.js +2 -1
- package/dist/image/jpeg2000-t1.cjs +21 -11
- package/dist/image/jpeg2000-t1.js +21 -11
- package/dist/image/jpeg2000-t2.cjs +12 -5
- package/dist/image/jpeg2000-t2.js +12 -5
- package/dist/image/jpeg2000-tagtree.cjs +8 -4
- package/dist/image/jpeg2000-tagtree.js +8 -4
- package/dist/image/jpeg2000.cjs +11 -6
- package/dist/image/jpeg2000.js +11 -6
- package/dist/image/png-decode.cjs +62 -39
- package/dist/image/png-decode.d.cts +3 -2
- package/dist/image/png-decode.d.ts +3 -2
- package/dist/image/png-decode.js +62 -39
- package/dist/image/png-filter.cjs +23 -15
- package/dist/image/png-filter.js +23 -15
- package/dist/images-read.cjs +41 -36
- package/dist/images-read.d.cts +2 -2
- package/dist/images-read.d.ts +2 -2
- package/dist/images-read.js +41 -36
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interpret.cjs +68 -40
- package/dist/interpret.d.cts +1 -1
- package/dist/interpret.d.ts +1 -1
- package/dist/interpret.js +68 -40
- package/dist/lexer.cjs +86 -51
- package/dist/lexer.js +86 -51
- package/dist/math-content-write.cjs +9 -5
- package/dist/math-content-write.js +9 -5
- package/dist/math-font-write.cjs +2 -1
- package/dist/math-font-write.d.cts +1 -1
- package/dist/math-font-write.d.ts +1 -1
- package/dist/math-font-write.js +2 -1
- package/dist/math-font.cjs +16 -7
- package/dist/math-font.js +16 -7
- package/dist/math-table.cjs +25 -13
- package/dist/math-table.js +25 -13
- package/dist/matrix.cjs +3 -2
- package/dist/matrix.js +3 -2
- package/dist/measure.cjs +1 -1
- package/dist/measure.js +1 -1
- package/dist/names.cjs +7 -7
- package/dist/names.d.cts +2 -2
- package/dist/names.d.ts +2 -2
- package/dist/names.js +7 -7
- package/dist/navigation.cjs +16 -12
- package/dist/navigation.d.cts +4 -4
- package/dist/navigation.d.ts +4 -4
- package/dist/navigation.js +16 -12
- package/dist/{objects-B7HdMeWS.d.cts → objects-e2RS3-yT.d.cts} +3 -3
- package/dist/{objects-B7HdMeWS.d.ts → objects-e2RS3-yT.d.ts} +3 -3
- package/dist/objects.d.cts +1 -1
- package/dist/objects.d.ts +1 -1
- package/dist/optional-content.d.cts +2 -2
- package/dist/optional-content.d.ts +2 -2
- package/dist/ot-layout-common.cjs +22 -15
- package/dist/ot-layout-common.js +22 -15
- package/dist/parse.cjs +7 -5
- package/dist/parse.d.cts +1 -1
- package/dist/parse.d.ts +1 -1
- package/dist/parse.js +7 -5
- package/dist/pdf-text.cjs +5 -2
- package/dist/pdf-text.js +5 -2
- package/dist/predictors.cjs +14 -9
- package/dist/predictors.d.cts +1 -1
- package/dist/predictors.d.ts +1 -1
- package/dist/predictors.js +14 -9
- package/dist/raster.cjs +30 -19
- package/dist/raster.d.cts +8 -8
- package/dist/raster.d.ts +8 -8
- package/dist/raster.js +30 -19
- package/dist/read.cjs +29 -16
- package/dist/read.js +29 -16
- package/dist/serialize.cjs +6 -2
- package/dist/serialize.d.cts +1 -1
- package/dist/serialize.d.ts +1 -1
- package/dist/serialize.js +6 -2
- package/dist/sfnt-subset.cjs +25 -13
- package/dist/sfnt-subset.d.cts +1 -1
- package/dist/sfnt-subset.d.ts +1 -1
- package/dist/sfnt-subset.js +25 -13
- package/dist/sfnt.cjs +20 -10
- package/dist/sfnt.js +20 -10
- package/dist/structure.d.cts +2 -2
- package/dist/structure.d.ts +2 -2
- package/dist/text-group.cjs +0 -1
- package/dist/text-group.js +0 -1
- package/dist/text-layout.d.cts +1 -1
- package/dist/text-layout.d.ts +1 -1
- package/dist/tounicode.cjs +4 -2
- package/dist/tounicode.d.cts +1 -1
- package/dist/tounicode.d.ts +1 -1
- package/dist/tounicode.js +4 -2
- package/dist/winansi.cjs +1 -1
- package/dist/winansi.js +1 -1
- package/dist/write.cjs +20 -12
- package/dist/write.d.cts +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.js +20 -12
- package/dist/xref.cjs +5 -2
- package/dist/xref.d.cts +1 -1
- package/dist/xref.d.ts +1 -1
- package/dist/xref.js +5 -2
- package/package.json +1 -1
package/dist/embedded-font.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-
|
|
1
|
+
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-ByD7ZiQS.js";
|
|
2
2
|
export { EmbeddedFace, EmbeddedFaceMetrics, EmbeddedFaceSubstitution, EmbeddedKern, EmbeddedShow, NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
package/dist/embedded-font.js
CHANGED
|
@@ -110,6 +110,8 @@ function parseHhea(font) {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
const CID_BYTE_LENGTH = 2;
|
|
113
|
+
const BITS_PER_BYTE = 8;
|
|
114
|
+
const BYTE_MASK = 255;
|
|
113
115
|
function encodeForShowEmbedded(text, face) {
|
|
114
116
|
const resolvedGlyphIds = [];
|
|
115
117
|
const substitutions = [];
|
|
@@ -136,8 +138,8 @@ function encodeForShowEmbedded(text, face) {
|
|
|
136
138
|
}
|
|
137
139
|
const codes = new Uint8Array(shownGlyphIds.length * CID_BYTE_LENGTH);
|
|
138
140
|
shownGlyphIds.forEach((glyphId, index) => {
|
|
139
|
-
codes[index * CID_BYTE_LENGTH] = glyphId >>
|
|
140
|
-
codes[index * CID_BYTE_LENGTH + 1] = glyphId &
|
|
141
|
+
codes[index * CID_BYTE_LENGTH] = glyphId >> BITS_PER_BYTE & BYTE_MASK;
|
|
142
|
+
codes[index * CID_BYTE_LENGTH + 1] = glyphId & BYTE_MASK;
|
|
141
143
|
});
|
|
142
144
|
return {
|
|
143
145
|
codes,
|
package/dist/encoding.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_afm_widths = require("./afm-widths-
|
|
2
|
+
const require_afm_widths = require("./afm-widths-BHD2HZIL.cjs");
|
|
3
3
|
exports.MACROMAN_GLYPH_NAMES = require_afm_widths.MACROMAN_GLYPH_NAMES;
|
|
4
4
|
exports.STANDARD_GLYPH_NAMES = require_afm_widths.STANDARD_GLYPH_NAMES;
|
|
5
5
|
exports.SYMBOL_GLYPH_NAMES = require_afm_widths.SYMBOL_GLYPH_NAMES;
|
package/dist/encoding.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SYMBOL_GLYPH_NAMES, c as glyphNameToUnicode, d as standardGlyphName, f as symbolGlyphName, i as STANDARD_GLYPH_NAMES, l as macRomanGlyphName, m as zapfDingbatsGlyphName, o as WINANSI_GLYPH_NAMES, p as winAnsiGlyphName, r as MACROMAN_GLYPH_NAMES, s as ZAPFDINGBATS_GLYPH_NAMES, u as namedEncodingGlyphName } from "./afm-widths-
|
|
1
|
+
import { a as SYMBOL_GLYPH_NAMES, c as glyphNameToUnicode, d as standardGlyphName, f as symbolGlyphName, i as STANDARD_GLYPH_NAMES, l as macRomanGlyphName, m as zapfDingbatsGlyphName, o as WINANSI_GLYPH_NAMES, p as winAnsiGlyphName, r as MACROMAN_GLYPH_NAMES, s as ZAPFDINGBATS_GLYPH_NAMES, u as namedEncodingGlyphName } from "./afm-widths-B_IGbGky.js";
|
|
2
2
|
export { MACROMAN_GLYPH_NAMES, STANDARD_GLYPH_NAMES, SYMBOL_GLYPH_NAMES, WINANSI_GLYPH_NAMES, ZAPFDINGBATS_GLYPH_NAMES, glyphNameToUnicode, macRomanGlyphName, namedEncodingGlyphName, standardGlyphName, symbolGlyphName, winAnsiGlyphName, zapfDingbatsGlyphName };
|
package/dist/encrypt-write.cjs
CHANGED
|
@@ -15,6 +15,8 @@ var PdfEncryptionError = class extends Error {
|
|
|
15
15
|
};
|
|
16
16
|
const RC4_128_KEY_BYTES = 16;
|
|
17
17
|
const R6_PASSWORD_MAX_BYTES = 127;
|
|
18
|
+
const BITS_PER_BYTE = 8;
|
|
19
|
+
const MAX_PERMISSION_BITS = 32;
|
|
18
20
|
const RESERVED_BIT_7_AND_8 = 192;
|
|
19
21
|
const DEPRECATED_ALWAYS_ONE_BIT_10 = 512;
|
|
20
22
|
const SCHEME_SPECS = {
|
|
@@ -43,11 +45,12 @@ const SCHEME_SPECS = {
|
|
|
43
45
|
method: "aes"
|
|
44
46
|
}
|
|
45
47
|
};
|
|
48
|
+
const MAX_PRINTABLE_ASCII = 127;
|
|
46
49
|
function legacyPasswordBytes(password) {
|
|
47
50
|
const bytes = new Uint8Array(password.length);
|
|
48
51
|
for (let i = 0; i < password.length; i++) {
|
|
49
52
|
const code = password.charCodeAt(i);
|
|
50
|
-
if (code >
|
|
53
|
+
if (code > MAX_PRINTABLE_ASCII) throw new PdfEncryptionError(`password contains character code ${String(code)}, outside the printable-ASCII range this writer supports for the rc4-40/rc4-128/aes-128 schemes; use a plain-ASCII password, or the aes-256 scheme, whose revision-6 passwords are Unicode`);
|
|
51
54
|
bytes[i] = code;
|
|
52
55
|
}
|
|
53
56
|
return bytes;
|
|
@@ -66,15 +69,16 @@ function permissionsToP(permissions) {
|
|
|
66
69
|
p |= DEPRECATED_ALWAYS_ONE_BIT_10;
|
|
67
70
|
if (permissions?.assemble ?? true) p |= 1024;
|
|
68
71
|
if (permissions?.printHighRes ?? true) p |= 2048;
|
|
69
|
-
for (let bit = 13; bit <=
|
|
72
|
+
for (let bit = 13; bit <= MAX_PERMISSION_BITS; bit++) p |= 1 << bit - 1;
|
|
70
73
|
return p;
|
|
71
74
|
}
|
|
75
|
+
const ITERATED_REVISION_MIN = 3;
|
|
72
76
|
function computeLegacyOwnerValue(ownerPassword, userPassword, revision, keyBytes) {
|
|
73
77
|
let digest = require_crypto_md5.md5(require_encrypt.padOrTruncatePassword(ownerPassword));
|
|
74
|
-
if (revision >=
|
|
78
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = require_crypto_md5.md5(digest);
|
|
75
79
|
const ownerKey = digest.subarray(0, keyBytes);
|
|
76
80
|
let value = require_crypto_rc4.rc4(ownerKey, require_encrypt.padOrTruncatePassword(userPassword));
|
|
77
|
-
if (revision >=
|
|
81
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 1; i <= 19; i++) {
|
|
78
82
|
const roundKey = Uint8Array.from(ownerKey, (byte) => byte ^ i);
|
|
79
83
|
value = require_crypto_rc4.rc4(roundKey, value);
|
|
80
84
|
}
|
|
@@ -125,9 +129,9 @@ function buildLegacyEncryptor(spec, options, fileId) {
|
|
|
125
129
|
["O", require_objects.pdfHexString(owner)],
|
|
126
130
|
["U", require_objects.pdfHexString(user)],
|
|
127
131
|
["P", require_objects.pdfNum(p)],
|
|
128
|
-
["Length", require_objects.pdfNum(spec.keyBytes *
|
|
132
|
+
["Length", require_objects.pdfNum(spec.keyBytes * BITS_PER_BYTE)]
|
|
129
133
|
]);
|
|
130
|
-
if (spec.v ===
|
|
134
|
+
if (spec.v === SCHEME_SPECS["aes-128"].v) {
|
|
131
135
|
entries.set("CF", require_objects.pdfDict({ StdCF: require_objects.pdfDict({
|
|
132
136
|
AuthEvent: require_objects.pdfName("DocOpen"),
|
|
133
137
|
CFM: require_objects.pdfName("AESV2"),
|
|
@@ -166,21 +170,21 @@ function buildAes256Encryptor(spec, options) {
|
|
|
166
170
|
]);
|
|
167
171
|
const ownerIntermediateKey = require_encrypt.hardenedHash(ownerPassword, ownerKeySalt, u, spec.r);
|
|
168
172
|
const oe = require_crypto_aes.aesCbcEncrypt(ownerIntermediateKey, require_encrypt.ZERO_IV, fileKey);
|
|
173
|
+
const PERMS_P_BYTES = 4;
|
|
174
|
+
const PERMS_SIGN_EXTENSION_BYTES = new Uint8Array(PERMS_P_BYTES).fill(255);
|
|
175
|
+
const PERMS_METADATA_FLAG_OFFSET = 8;
|
|
176
|
+
const ENCRYPT_METADATA_TRUE_ASCII = 84;
|
|
177
|
+
const ENCRYPT_METADATA_FALSE_ASCII = 70;
|
|
178
|
+
const ADB_MARKER_OFFSET = 9;
|
|
179
|
+
const ADB_MARKER_BYTES = Uint8Array.from("adb", (c) => c.charCodeAt(0));
|
|
180
|
+
const PERMS_RANDOM_BYTES = 4;
|
|
181
|
+
const PERMS_RANDOM_OFFSET = ADB_MARKER_OFFSET + ADB_MARKER_BYTES.length;
|
|
169
182
|
const permsBlock = /* @__PURE__ */ new Uint8Array(16);
|
|
170
183
|
permsBlock.set(require_encrypt.permissionsBytes(p), 0);
|
|
171
|
-
permsBlock.set(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
255
|
|
176
|
-
], 4);
|
|
177
|
-
permsBlock[8] = encryptMetadata ? 84 : 70;
|
|
178
|
-
permsBlock.set([
|
|
179
|
-
97,
|
|
180
|
-
100,
|
|
181
|
-
98
|
|
182
|
-
], 9);
|
|
183
|
-
permsBlock.set(require_crypto_random.randomBytes(4), 12);
|
|
184
|
+
permsBlock.set(PERMS_SIGN_EXTENSION_BYTES, PERMS_P_BYTES);
|
|
185
|
+
permsBlock[PERMS_METADATA_FLAG_OFFSET] = encryptMetadata ? ENCRYPT_METADATA_TRUE_ASCII : ENCRYPT_METADATA_FALSE_ASCII;
|
|
186
|
+
permsBlock.set(ADB_MARKER_BYTES, ADB_MARKER_OFFSET);
|
|
187
|
+
permsBlock.set(require_crypto_random.randomBytes(PERMS_RANDOM_BYTES), PERMS_RANDOM_OFFSET);
|
|
184
188
|
const perms = require_crypto_aes.aesCbcEncrypt(fileKey, require_encrypt.ZERO_IV, permsBlock);
|
|
185
189
|
const entries = /* @__PURE__ */ new Map([
|
|
186
190
|
["Filter", require_objects.pdfName("Standard")],
|
|
@@ -192,7 +196,7 @@ function buildAes256Encryptor(spec, options) {
|
|
|
192
196
|
["UE", require_objects.pdfHexString(ue)],
|
|
193
197
|
["P", require_objects.pdfNum(p)],
|
|
194
198
|
["Perms", require_objects.pdfHexString(perms)],
|
|
195
|
-
["Length", require_objects.pdfNum(spec.keyBytes *
|
|
199
|
+
["Length", require_objects.pdfNum(spec.keyBytes * BITS_PER_BYTE)],
|
|
196
200
|
["CF", require_objects.pdfDict({ StdCF: require_objects.pdfDict({
|
|
197
201
|
AuthEvent: require_objects.pdfName("DocOpen"),
|
|
198
202
|
CFM: require_objects.pdfName("AESV3"),
|
|
@@ -207,7 +211,7 @@ function buildAes256Encryptor(spec, options) {
|
|
|
207
211
|
function createStandardEncryptor(options, fileId) {
|
|
208
212
|
const scheme = options.scheme ?? "aes-256";
|
|
209
213
|
const spec = SCHEME_SPECS[scheme];
|
|
210
|
-
return spec.v ===
|
|
214
|
+
return spec.v === SCHEME_SPECS["aes-256"].v ? buildAes256Encryptor(spec, options) : buildLegacyEncryptor(spec, options, fileId);
|
|
211
215
|
}
|
|
212
216
|
function encryptIndirectObject(value, num, gen, encryptor) {
|
|
213
217
|
if (value.kind === "string") return {
|
package/dist/encrypt-write.d.cts
CHANGED
package/dist/encrypt-write.d.ts
CHANGED
package/dist/encrypt-write.js
CHANGED
|
@@ -14,6 +14,8 @@ var PdfEncryptionError = class extends Error {
|
|
|
14
14
|
};
|
|
15
15
|
const RC4_128_KEY_BYTES = 16;
|
|
16
16
|
const R6_PASSWORD_MAX_BYTES = 127;
|
|
17
|
+
const BITS_PER_BYTE = 8;
|
|
18
|
+
const MAX_PERMISSION_BITS = 32;
|
|
17
19
|
const RESERVED_BIT_7_AND_8 = 192;
|
|
18
20
|
const DEPRECATED_ALWAYS_ONE_BIT_10 = 512;
|
|
19
21
|
const SCHEME_SPECS = {
|
|
@@ -42,11 +44,12 @@ const SCHEME_SPECS = {
|
|
|
42
44
|
method: "aes"
|
|
43
45
|
}
|
|
44
46
|
};
|
|
47
|
+
const MAX_PRINTABLE_ASCII = 127;
|
|
45
48
|
function legacyPasswordBytes(password) {
|
|
46
49
|
const bytes = new Uint8Array(password.length);
|
|
47
50
|
for (let i = 0; i < password.length; i++) {
|
|
48
51
|
const code = password.charCodeAt(i);
|
|
49
|
-
if (code >
|
|
52
|
+
if (code > MAX_PRINTABLE_ASCII) throw new PdfEncryptionError(`password contains character code ${String(code)}, outside the printable-ASCII range this writer supports for the rc4-40/rc4-128/aes-128 schemes; use a plain-ASCII password, or the aes-256 scheme, whose revision-6 passwords are Unicode`);
|
|
50
53
|
bytes[i] = code;
|
|
51
54
|
}
|
|
52
55
|
return bytes;
|
|
@@ -65,15 +68,16 @@ function permissionsToP(permissions) {
|
|
|
65
68
|
p |= DEPRECATED_ALWAYS_ONE_BIT_10;
|
|
66
69
|
if (permissions?.assemble ?? true) p |= 1024;
|
|
67
70
|
if (permissions?.printHighRes ?? true) p |= 2048;
|
|
68
|
-
for (let bit = 13; bit <=
|
|
71
|
+
for (let bit = 13; bit <= MAX_PERMISSION_BITS; bit++) p |= 1 << bit - 1;
|
|
69
72
|
return p;
|
|
70
73
|
}
|
|
74
|
+
const ITERATED_REVISION_MIN = 3;
|
|
71
75
|
function computeLegacyOwnerValue(ownerPassword, userPassword, revision, keyBytes) {
|
|
72
76
|
let digest = md5(padOrTruncatePassword(ownerPassword));
|
|
73
|
-
if (revision >=
|
|
77
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = md5(digest);
|
|
74
78
|
const ownerKey = digest.subarray(0, keyBytes);
|
|
75
79
|
let value = rc4(ownerKey, padOrTruncatePassword(userPassword));
|
|
76
|
-
if (revision >=
|
|
80
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 1; i <= 19; i++) {
|
|
77
81
|
const roundKey = Uint8Array.from(ownerKey, (byte) => byte ^ i);
|
|
78
82
|
value = rc4(roundKey, value);
|
|
79
83
|
}
|
|
@@ -124,9 +128,9 @@ function buildLegacyEncryptor(spec, options, fileId) {
|
|
|
124
128
|
["O", pdfHexString(owner)],
|
|
125
129
|
["U", pdfHexString(user)],
|
|
126
130
|
["P", pdfNum(p)],
|
|
127
|
-
["Length", pdfNum(spec.keyBytes *
|
|
131
|
+
["Length", pdfNum(spec.keyBytes * BITS_PER_BYTE)]
|
|
128
132
|
]);
|
|
129
|
-
if (spec.v ===
|
|
133
|
+
if (spec.v === SCHEME_SPECS["aes-128"].v) {
|
|
130
134
|
entries.set("CF", pdfDict({ StdCF: pdfDict({
|
|
131
135
|
AuthEvent: pdfName("DocOpen"),
|
|
132
136
|
CFM: pdfName("AESV2"),
|
|
@@ -165,21 +169,21 @@ function buildAes256Encryptor(spec, options) {
|
|
|
165
169
|
]);
|
|
166
170
|
const ownerIntermediateKey = hardenedHash(ownerPassword, ownerKeySalt, u, spec.r);
|
|
167
171
|
const oe = aesCbcEncrypt(ownerIntermediateKey, ZERO_IV, fileKey);
|
|
172
|
+
const PERMS_P_BYTES = 4;
|
|
173
|
+
const PERMS_SIGN_EXTENSION_BYTES = new Uint8Array(PERMS_P_BYTES).fill(255);
|
|
174
|
+
const PERMS_METADATA_FLAG_OFFSET = 8;
|
|
175
|
+
const ENCRYPT_METADATA_TRUE_ASCII = 84;
|
|
176
|
+
const ENCRYPT_METADATA_FALSE_ASCII = 70;
|
|
177
|
+
const ADB_MARKER_OFFSET = 9;
|
|
178
|
+
const ADB_MARKER_BYTES = Uint8Array.from("adb", (c) => c.charCodeAt(0));
|
|
179
|
+
const PERMS_RANDOM_BYTES = 4;
|
|
180
|
+
const PERMS_RANDOM_OFFSET = ADB_MARKER_OFFSET + ADB_MARKER_BYTES.length;
|
|
168
181
|
const permsBlock = /* @__PURE__ */ new Uint8Array(16);
|
|
169
182
|
permsBlock.set(permissionsBytes(p), 0);
|
|
170
|
-
permsBlock.set(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
255
|
|
175
|
-
], 4);
|
|
176
|
-
permsBlock[8] = encryptMetadata ? 84 : 70;
|
|
177
|
-
permsBlock.set([
|
|
178
|
-
97,
|
|
179
|
-
100,
|
|
180
|
-
98
|
|
181
|
-
], 9);
|
|
182
|
-
permsBlock.set(randomBytes(4), 12);
|
|
183
|
+
permsBlock.set(PERMS_SIGN_EXTENSION_BYTES, PERMS_P_BYTES);
|
|
184
|
+
permsBlock[PERMS_METADATA_FLAG_OFFSET] = encryptMetadata ? ENCRYPT_METADATA_TRUE_ASCII : ENCRYPT_METADATA_FALSE_ASCII;
|
|
185
|
+
permsBlock.set(ADB_MARKER_BYTES, ADB_MARKER_OFFSET);
|
|
186
|
+
permsBlock.set(randomBytes(PERMS_RANDOM_BYTES), PERMS_RANDOM_OFFSET);
|
|
183
187
|
const perms = aesCbcEncrypt(fileKey, ZERO_IV, permsBlock);
|
|
184
188
|
const entries = /* @__PURE__ */ new Map([
|
|
185
189
|
["Filter", pdfName("Standard")],
|
|
@@ -191,7 +195,7 @@ function buildAes256Encryptor(spec, options) {
|
|
|
191
195
|
["UE", pdfHexString(ue)],
|
|
192
196
|
["P", pdfNum(p)],
|
|
193
197
|
["Perms", pdfHexString(perms)],
|
|
194
|
-
["Length", pdfNum(spec.keyBytes *
|
|
198
|
+
["Length", pdfNum(spec.keyBytes * BITS_PER_BYTE)],
|
|
195
199
|
["CF", pdfDict({ StdCF: pdfDict({
|
|
196
200
|
AuthEvent: pdfName("DocOpen"),
|
|
197
201
|
CFM: pdfName("AESV3"),
|
|
@@ -206,7 +210,7 @@ function buildAes256Encryptor(spec, options) {
|
|
|
206
210
|
function createStandardEncryptor(options, fileId) {
|
|
207
211
|
const scheme = options.scheme ?? "aes-256";
|
|
208
212
|
const spec = SCHEME_SPECS[scheme];
|
|
209
|
-
return spec.v ===
|
|
213
|
+
return spec.v === SCHEME_SPECS["aes-256"].v ? buildAes256Encryptor(spec, options) : buildLegacyEncryptor(spec, options, fileId);
|
|
210
214
|
}
|
|
211
215
|
function encryptIndirectObject(value, num, gen, encryptor) {
|
|
212
216
|
if (value.kind === "string") return {
|
package/dist/encrypt.cjs
CHANGED
|
@@ -7,46 +7,18 @@ const require_crypto_md5 = require("./crypto/md5.cjs");
|
|
|
7
7
|
const require_crypto_rc4 = require("./crypto/rc4.cjs");
|
|
8
8
|
const require_crypto_sha2 = require("./crypto/sha2.cjs");
|
|
9
9
|
//#region src/encrypt.ts
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
86,
|
|
23
|
-
255,
|
|
24
|
-
250,
|
|
25
|
-
1,
|
|
26
|
-
8,
|
|
27
|
-
46,
|
|
28
|
-
46,
|
|
29
|
-
0,
|
|
30
|
-
182,
|
|
31
|
-
208,
|
|
32
|
-
104,
|
|
33
|
-
62,
|
|
34
|
-
128,
|
|
35
|
-
47,
|
|
36
|
-
12,
|
|
37
|
-
169,
|
|
38
|
-
254,
|
|
39
|
-
100,
|
|
40
|
-
83,
|
|
41
|
-
105,
|
|
42
|
-
122
|
|
43
|
-
]);
|
|
44
|
-
const AES_OBJECT_KEY_SALT = new Uint8Array([
|
|
45
|
-
115,
|
|
46
|
-
65,
|
|
47
|
-
108,
|
|
48
|
-
84
|
|
49
|
-
]);
|
|
10
|
+
const HEX_RADIX = 16;
|
|
11
|
+
const HEX_CHARS_PER_BYTE = 2;
|
|
12
|
+
function bytesFromHex(hex) {
|
|
13
|
+
const bytes = new Uint8Array(hex.length / HEX_CHARS_PER_BYTE);
|
|
14
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
15
|
+
const start = i * HEX_CHARS_PER_BYTE;
|
|
16
|
+
bytes[i] = parseInt(hex.substring(start, start + HEX_CHARS_PER_BYTE), HEX_RADIX);
|
|
17
|
+
}
|
|
18
|
+
return bytes;
|
|
19
|
+
}
|
|
20
|
+
const PASSWORD_PADDING = bytesFromHex("28bf4e5e4e758a4164004e56fffa01082e2e00b6d0683e802f0ca9fe6453697a");
|
|
21
|
+
const AES_OBJECT_KEY_SALT = new TextEncoder().encode("sAlT");
|
|
50
22
|
const MD5_DIGEST_BYTES = 16;
|
|
51
23
|
const OBJECT_KEY_EXTRA_BYTES = 5;
|
|
52
24
|
const LEGACY_KEY_ITERATIONS = 50;
|
|
@@ -60,6 +32,9 @@ const RC4_40_KEY_BYTES = 5;
|
|
|
60
32
|
const AESV2_KEY_BYTES = 16;
|
|
61
33
|
const DEFAULT_KEY_BITS = 40;
|
|
62
34
|
const BITS_PER_BYTE = 8;
|
|
35
|
+
const BYTE_MASK = 255;
|
|
36
|
+
const UINT32_BYTE_2_SHIFT = 16;
|
|
37
|
+
const UINT32_BYTE_3_SHIFT = 24;
|
|
63
38
|
const MAX_RC4_KEY_BYTES = 16;
|
|
64
39
|
const ZERO_IV = /* @__PURE__ */ new Uint8Array(16);
|
|
65
40
|
function requireStringBytes(dict, key) {
|
|
@@ -74,10 +49,10 @@ function bytesEqual(a, b, length) {
|
|
|
74
49
|
function permissionsBytes(p) {
|
|
75
50
|
const unsigned = Math.trunc(p) >>> 0;
|
|
76
51
|
return new Uint8Array([
|
|
77
|
-
unsigned &
|
|
78
|
-
unsigned >>>
|
|
79
|
-
unsigned >>>
|
|
80
|
-
unsigned >>>
|
|
52
|
+
unsigned & BYTE_MASK,
|
|
53
|
+
unsigned >>> BITS_PER_BYTE & BYTE_MASK,
|
|
54
|
+
unsigned >>> UINT32_BYTE_2_SHIFT & BYTE_MASK,
|
|
55
|
+
unsigned >>> UINT32_BYTE_3_SHIFT & BYTE_MASK
|
|
81
56
|
]);
|
|
82
57
|
}
|
|
83
58
|
function padOrTruncatePassword(password) {
|
|
@@ -85,6 +60,9 @@ function padOrTruncatePassword(password) {
|
|
|
85
60
|
padded.set(password.subarray(0, PASSWORD_PADDING.length));
|
|
86
61
|
return padded;
|
|
87
62
|
}
|
|
63
|
+
const METADATA_FLAG_MIN_REVISION = 4;
|
|
64
|
+
const METADATA_NOT_ENCRYPTED_FLAG_BYTES = 4;
|
|
65
|
+
const ITERATED_REVISION_MIN = 3;
|
|
88
66
|
function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissions, fileId, revision, keyBytes, encryptMetadata) {
|
|
89
67
|
const parts = [
|
|
90
68
|
paddedPassword,
|
|
@@ -92,14 +70,9 @@ function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissio
|
|
|
92
70
|
permissionsBytes(permissions),
|
|
93
71
|
fileId
|
|
94
72
|
];
|
|
95
|
-
if (revision >=
|
|
96
|
-
255,
|
|
97
|
-
255,
|
|
98
|
-
255,
|
|
99
|
-
255
|
|
100
|
-
]));
|
|
73
|
+
if (revision >= METADATA_FLAG_MIN_REVISION && !encryptMetadata) parts.push(new Uint8Array(METADATA_NOT_ENCRYPTED_FLAG_BYTES).fill(BYTE_MASK));
|
|
101
74
|
let digest = require_crypto_md5.md5(require_bytes_writer.concatBytes(parts));
|
|
102
|
-
if (revision >=
|
|
75
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = require_crypto_md5.md5(digest.subarray(0, keyBytes));
|
|
103
76
|
return digest.subarray(0, keyBytes);
|
|
104
77
|
}
|
|
105
78
|
function computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata) {
|
|
@@ -120,13 +93,16 @@ function legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey) {
|
|
|
120
93
|
const storedUser = requireStringBytes(encryptDict, "U");
|
|
121
94
|
return bytesEqual(legacyUserValueCore(fileKey, fileId, revision), storedUser, revision === 2 ? PASSWORD_PADDING.length : 16);
|
|
122
95
|
}
|
|
96
|
+
const HARDENED_HASH_SHA256_ONLY_REVISION = 5;
|
|
97
|
+
const STANDARD_AESV3_REVISION = 6;
|
|
98
|
+
const HASH_SELECTOR_MODULUS = 3;
|
|
123
99
|
function hardenedHash(password, salt, userData, revision) {
|
|
124
100
|
let k = require_crypto_sha2.sha256(require_bytes_writer.concatBytes([
|
|
125
101
|
password,
|
|
126
102
|
salt,
|
|
127
103
|
userData
|
|
128
104
|
]));
|
|
129
|
-
if (revision ===
|
|
105
|
+
if (revision === HARDENED_HASH_SHA256_ONLY_REVISION) return k;
|
|
130
106
|
let e;
|
|
131
107
|
let round = 0;
|
|
132
108
|
do {
|
|
@@ -140,7 +116,7 @@ function hardenedHash(password, salt, userData, revision) {
|
|
|
140
116
|
e = require_crypto_aes.aesCbcEncrypt(k.subarray(0, 16), k.subarray(16, 32), k1);
|
|
141
117
|
let sum = 0;
|
|
142
118
|
for (let i = 0; i < 16; i++) sum += e[i];
|
|
143
|
-
const selector = sum %
|
|
119
|
+
const selector = sum % HASH_SELECTOR_MODULUS;
|
|
144
120
|
k = selector === 0 ? require_crypto_sha2.sha256(e) : selector === 1 ? require_crypto_sha2.sha384(e) : require_crypto_sha2.sha512(e);
|
|
145
121
|
round++;
|
|
146
122
|
} while (round < R6_MINIMUM_ROUNDS || e[e.length - 1] > round - R6_TERMINATION_MARGIN);
|
|
@@ -180,14 +156,17 @@ function cryptFilterKeyBytes(encryptDict, declaredBits) {
|
|
|
180
156
|
if (declared === void 0) return Math.floor(declaredBits / BITS_PER_BYTE);
|
|
181
157
|
return declared >= DEFAULT_KEY_BITS ? Math.floor(declared / BITS_PER_BYTE) : declared;
|
|
182
158
|
}
|
|
159
|
+
const V5_HANDLER_VERSION = 5;
|
|
160
|
+
const V4_HANDLER_VERSION = 4;
|
|
161
|
+
const LEGACY_MAX_REVISION = 4;
|
|
183
162
|
function setUpHandler(encryptDict, fileId) {
|
|
184
163
|
const version = require_objects.asNumber(require_objects.dictGet(encryptDict, "V")) ?? 0;
|
|
185
164
|
const revision = require_objects.asNumber(require_objects.dictGet(encryptDict, "R")) ?? 0;
|
|
186
165
|
const declaredBits = require_objects.asNumber(require_objects.dictGet(encryptDict, "Length")) ?? DEFAULT_KEY_BITS;
|
|
187
166
|
const encryptMetadataEntry = require_objects.dictGet(encryptDict, "EncryptMetadata");
|
|
188
167
|
const encryptMetadata = encryptMetadataEntry?.kind === "bool" ? encryptMetadataEntry.value : true;
|
|
189
|
-
if (version ===
|
|
190
|
-
if (revision !==
|
|
168
|
+
if (version === V5_HANDLER_VERSION) {
|
|
169
|
+
if (revision !== HARDENED_HASH_SHA256_ONLY_REVISION && revision !== STANDARD_AESV3_REVISION) throw new require_diagnostics.PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
|
|
191
170
|
return {
|
|
192
171
|
fileKey: computeAesV3FileKey(encryptDict, revision),
|
|
193
172
|
streamMethod: methodFromCryptFilterName(encryptDict, "StmF"),
|
|
@@ -196,10 +175,10 @@ function setUpHandler(encryptDict, fileId) {
|
|
|
196
175
|
encryptMetadata
|
|
197
176
|
};
|
|
198
177
|
}
|
|
199
|
-
if (version !== 1 && version !== 2 && version !==
|
|
200
|
-
if (revision < 2 || revision >
|
|
201
|
-
const streamMethod = version ===
|
|
202
|
-
const stringMethod = version ===
|
|
178
|
+
if (version !== 1 && version !== 2 && version !== V4_HANDLER_VERSION) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
|
|
179
|
+
if (revision < 2 || revision > LEGACY_MAX_REVISION) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
|
|
180
|
+
const streamMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
|
|
181
|
+
const stringMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
|
|
203
182
|
const keyBytes = version === 1 ? 5 : version === 2 ? Math.floor(declaredBits / BITS_PER_BYTE) : cryptFilterKeyBytes(encryptDict, declaredBits);
|
|
204
183
|
if (keyBytes < 5 || keyBytes > MAX_RC4_KEY_BYTES) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary declares a ${String(keyBytes * BITS_PER_BYTE)}-bit file key, outside the 40-128 bit range /V ${String(version)} permits`);
|
|
205
184
|
const fileKey = computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata);
|
|
@@ -214,11 +193,11 @@ function setUpHandler(encryptDict, fileId) {
|
|
|
214
193
|
}
|
|
215
194
|
function objectKey(fileKey, num, gen, method) {
|
|
216
195
|
const extra = new Uint8Array(OBJECT_KEY_EXTRA_BYTES);
|
|
217
|
-
extra[0] = num &
|
|
218
|
-
extra[1] = num >>>
|
|
219
|
-
extra[2] = num >>>
|
|
220
|
-
extra[3] = gen &
|
|
221
|
-
extra[4] = gen >>>
|
|
196
|
+
extra[0] = num & BYTE_MASK;
|
|
197
|
+
extra[1] = num >>> BITS_PER_BYTE & BYTE_MASK;
|
|
198
|
+
extra[2] = num >>> UINT32_BYTE_2_SHIFT & BYTE_MASK;
|
|
199
|
+
extra[3] = gen & BYTE_MASK;
|
|
200
|
+
extra[4] = gen >>> BITS_PER_BYTE & BYTE_MASK;
|
|
222
201
|
return require_crypto_md5.md5(require_bytes_writer.concatBytes(method === "aes" ? [
|
|
223
202
|
fileKey,
|
|
224
203
|
extra,
|
package/dist/encrypt.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PdfDiagnosticSink } from "./diagnostics.cjs";
|
|
2
|
-
import { n as PdfDict } from "./objects-
|
|
2
|
+
import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
|
|
3
3
|
//#region src/encrypt.d.ts
|
|
4
4
|
type CipherMethod = "identity" | "rc4" | "aes";
|
|
5
5
|
interface PdfDecryptor {
|
package/dist/encrypt.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PdfDiagnosticSink } from "./diagnostics.js";
|
|
2
|
-
import { n as PdfDict } from "./objects-
|
|
2
|
+
import { n as PdfDict } from "./objects-e2RS3-yT.js";
|
|
3
3
|
//#region src/encrypt.d.ts
|
|
4
4
|
type CipherMethod = "identity" | "rc4" | "aes";
|
|
5
5
|
interface PdfDecryptor {
|
package/dist/encrypt.js
CHANGED
|
@@ -6,46 +6,18 @@ import { md5 } from "./crypto/md5.js";
|
|
|
6
6
|
import { rc4 } from "./crypto/rc4.js";
|
|
7
7
|
import { sha256, sha384, sha512 } from "./crypto/sha2.js";
|
|
8
8
|
//#region src/encrypt.ts
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
86,
|
|
22
|
-
255,
|
|
23
|
-
250,
|
|
24
|
-
1,
|
|
25
|
-
8,
|
|
26
|
-
46,
|
|
27
|
-
46,
|
|
28
|
-
0,
|
|
29
|
-
182,
|
|
30
|
-
208,
|
|
31
|
-
104,
|
|
32
|
-
62,
|
|
33
|
-
128,
|
|
34
|
-
47,
|
|
35
|
-
12,
|
|
36
|
-
169,
|
|
37
|
-
254,
|
|
38
|
-
100,
|
|
39
|
-
83,
|
|
40
|
-
105,
|
|
41
|
-
122
|
|
42
|
-
]);
|
|
43
|
-
const AES_OBJECT_KEY_SALT = new Uint8Array([
|
|
44
|
-
115,
|
|
45
|
-
65,
|
|
46
|
-
108,
|
|
47
|
-
84
|
|
48
|
-
]);
|
|
9
|
+
const HEX_RADIX = 16;
|
|
10
|
+
const HEX_CHARS_PER_BYTE = 2;
|
|
11
|
+
function bytesFromHex(hex) {
|
|
12
|
+
const bytes = new Uint8Array(hex.length / HEX_CHARS_PER_BYTE);
|
|
13
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
14
|
+
const start = i * HEX_CHARS_PER_BYTE;
|
|
15
|
+
bytes[i] = parseInt(hex.substring(start, start + HEX_CHARS_PER_BYTE), HEX_RADIX);
|
|
16
|
+
}
|
|
17
|
+
return bytes;
|
|
18
|
+
}
|
|
19
|
+
const PASSWORD_PADDING = bytesFromHex("28bf4e5e4e758a4164004e56fffa01082e2e00b6d0683e802f0ca9fe6453697a");
|
|
20
|
+
const AES_OBJECT_KEY_SALT = new TextEncoder().encode("sAlT");
|
|
49
21
|
const MD5_DIGEST_BYTES = 16;
|
|
50
22
|
const OBJECT_KEY_EXTRA_BYTES = 5;
|
|
51
23
|
const LEGACY_KEY_ITERATIONS = 50;
|
|
@@ -59,6 +31,9 @@ const RC4_40_KEY_BYTES = 5;
|
|
|
59
31
|
const AESV2_KEY_BYTES = 16;
|
|
60
32
|
const DEFAULT_KEY_BITS = 40;
|
|
61
33
|
const BITS_PER_BYTE = 8;
|
|
34
|
+
const BYTE_MASK = 255;
|
|
35
|
+
const UINT32_BYTE_2_SHIFT = 16;
|
|
36
|
+
const UINT32_BYTE_3_SHIFT = 24;
|
|
62
37
|
const MAX_RC4_KEY_BYTES = 16;
|
|
63
38
|
const ZERO_IV = /* @__PURE__ */ new Uint8Array(16);
|
|
64
39
|
function requireStringBytes(dict, key) {
|
|
@@ -73,10 +48,10 @@ function bytesEqual(a, b, length) {
|
|
|
73
48
|
function permissionsBytes(p) {
|
|
74
49
|
const unsigned = Math.trunc(p) >>> 0;
|
|
75
50
|
return new Uint8Array([
|
|
76
|
-
unsigned &
|
|
77
|
-
unsigned >>>
|
|
78
|
-
unsigned >>>
|
|
79
|
-
unsigned >>>
|
|
51
|
+
unsigned & BYTE_MASK,
|
|
52
|
+
unsigned >>> BITS_PER_BYTE & BYTE_MASK,
|
|
53
|
+
unsigned >>> UINT32_BYTE_2_SHIFT & BYTE_MASK,
|
|
54
|
+
unsigned >>> UINT32_BYTE_3_SHIFT & BYTE_MASK
|
|
80
55
|
]);
|
|
81
56
|
}
|
|
82
57
|
function padOrTruncatePassword(password) {
|
|
@@ -84,6 +59,9 @@ function padOrTruncatePassword(password) {
|
|
|
84
59
|
padded.set(password.subarray(0, PASSWORD_PADDING.length));
|
|
85
60
|
return padded;
|
|
86
61
|
}
|
|
62
|
+
const METADATA_FLAG_MIN_REVISION = 4;
|
|
63
|
+
const METADATA_NOT_ENCRYPTED_FLAG_BYTES = 4;
|
|
64
|
+
const ITERATED_REVISION_MIN = 3;
|
|
87
65
|
function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissions, fileId, revision, keyBytes, encryptMetadata) {
|
|
88
66
|
const parts = [
|
|
89
67
|
paddedPassword,
|
|
@@ -91,14 +69,9 @@ function computeLegacyFileKeyFromPaddedPassword(paddedPassword, owner, permissio
|
|
|
91
69
|
permissionsBytes(permissions),
|
|
92
70
|
fileId
|
|
93
71
|
];
|
|
94
|
-
if (revision >=
|
|
95
|
-
255,
|
|
96
|
-
255,
|
|
97
|
-
255,
|
|
98
|
-
255
|
|
99
|
-
]));
|
|
72
|
+
if (revision >= METADATA_FLAG_MIN_REVISION && !encryptMetadata) parts.push(new Uint8Array(METADATA_NOT_ENCRYPTED_FLAG_BYTES).fill(BYTE_MASK));
|
|
100
73
|
let digest = md5(concatBytes(parts));
|
|
101
|
-
if (revision >=
|
|
74
|
+
if (revision >= ITERATED_REVISION_MIN) for (let i = 0; i < 50; i++) digest = md5(digest.subarray(0, keyBytes));
|
|
102
75
|
return digest.subarray(0, keyBytes);
|
|
103
76
|
}
|
|
104
77
|
function computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata) {
|
|
@@ -119,13 +92,16 @@ function legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey) {
|
|
|
119
92
|
const storedUser = requireStringBytes(encryptDict, "U");
|
|
120
93
|
return bytesEqual(legacyUserValueCore(fileKey, fileId, revision), storedUser, revision === 2 ? PASSWORD_PADDING.length : 16);
|
|
121
94
|
}
|
|
95
|
+
const HARDENED_HASH_SHA256_ONLY_REVISION = 5;
|
|
96
|
+
const STANDARD_AESV3_REVISION = 6;
|
|
97
|
+
const HASH_SELECTOR_MODULUS = 3;
|
|
122
98
|
function hardenedHash(password, salt, userData, revision) {
|
|
123
99
|
let k = sha256(concatBytes([
|
|
124
100
|
password,
|
|
125
101
|
salt,
|
|
126
102
|
userData
|
|
127
103
|
]));
|
|
128
|
-
if (revision ===
|
|
104
|
+
if (revision === HARDENED_HASH_SHA256_ONLY_REVISION) return k;
|
|
129
105
|
let e;
|
|
130
106
|
let round = 0;
|
|
131
107
|
do {
|
|
@@ -139,7 +115,7 @@ function hardenedHash(password, salt, userData, revision) {
|
|
|
139
115
|
e = aesCbcEncrypt(k.subarray(0, 16), k.subarray(16, 32), k1);
|
|
140
116
|
let sum = 0;
|
|
141
117
|
for (let i = 0; i < 16; i++) sum += e[i];
|
|
142
|
-
const selector = sum %
|
|
118
|
+
const selector = sum % HASH_SELECTOR_MODULUS;
|
|
143
119
|
k = selector === 0 ? sha256(e) : selector === 1 ? sha384(e) : sha512(e);
|
|
144
120
|
round++;
|
|
145
121
|
} while (round < R6_MINIMUM_ROUNDS || e[e.length - 1] > round - R6_TERMINATION_MARGIN);
|
|
@@ -179,14 +155,17 @@ function cryptFilterKeyBytes(encryptDict, declaredBits) {
|
|
|
179
155
|
if (declared === void 0) return Math.floor(declaredBits / BITS_PER_BYTE);
|
|
180
156
|
return declared >= DEFAULT_KEY_BITS ? Math.floor(declared / BITS_PER_BYTE) : declared;
|
|
181
157
|
}
|
|
158
|
+
const V5_HANDLER_VERSION = 5;
|
|
159
|
+
const V4_HANDLER_VERSION = 4;
|
|
160
|
+
const LEGACY_MAX_REVISION = 4;
|
|
182
161
|
function setUpHandler(encryptDict, fileId) {
|
|
183
162
|
const version = asNumber(dictGet(encryptDict, "V")) ?? 0;
|
|
184
163
|
const revision = asNumber(dictGet(encryptDict, "R")) ?? 0;
|
|
185
164
|
const declaredBits = asNumber(dictGet(encryptDict, "Length")) ?? DEFAULT_KEY_BITS;
|
|
186
165
|
const encryptMetadataEntry = dictGet(encryptDict, "EncryptMetadata");
|
|
187
166
|
const encryptMetadata = encryptMetadataEntry?.kind === "bool" ? encryptMetadataEntry.value : true;
|
|
188
|
-
if (version ===
|
|
189
|
-
if (revision !==
|
|
167
|
+
if (version === V5_HANDLER_VERSION) {
|
|
168
|
+
if (revision !== HARDENED_HASH_SHA256_ONLY_REVISION && revision !== STANDARD_AESV3_REVISION) throw new PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
|
|
190
169
|
return {
|
|
191
170
|
fileKey: computeAesV3FileKey(encryptDict, revision),
|
|
192
171
|
streamMethod: methodFromCryptFilterName(encryptDict, "StmF"),
|
|
@@ -195,10 +174,10 @@ function setUpHandler(encryptDict, fileId) {
|
|
|
195
174
|
encryptMetadata
|
|
196
175
|
};
|
|
197
176
|
}
|
|
198
|
-
if (version !== 1 && version !== 2 && version !==
|
|
199
|
-
if (revision < 2 || revision >
|
|
200
|
-
const streamMethod = version ===
|
|
201
|
-
const stringMethod = version ===
|
|
177
|
+
if (version !== 1 && version !== 2 && version !== V4_HANDLER_VERSION) throw new PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
|
|
178
|
+
if (revision < 2 || revision > LEGACY_MAX_REVISION) throw new PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
|
|
179
|
+
const streamMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
|
|
180
|
+
const stringMethod = version === V4_HANDLER_VERSION ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
|
|
202
181
|
const keyBytes = version === 1 ? 5 : version === 2 ? Math.floor(declaredBits / BITS_PER_BYTE) : cryptFilterKeyBytes(encryptDict, declaredBits);
|
|
203
182
|
if (keyBytes < 5 || keyBytes > MAX_RC4_KEY_BYTES) throw new PdfEncryptedError(`the /Encrypt dictionary declares a ${String(keyBytes * BITS_PER_BYTE)}-bit file key, outside the 40-128 bit range /V ${String(version)} permits`);
|
|
204
183
|
const fileKey = computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata);
|
|
@@ -213,11 +192,11 @@ function setUpHandler(encryptDict, fileId) {
|
|
|
213
192
|
}
|
|
214
193
|
function objectKey(fileKey, num, gen, method) {
|
|
215
194
|
const extra = new Uint8Array(OBJECT_KEY_EXTRA_BYTES);
|
|
216
|
-
extra[0] = num &
|
|
217
|
-
extra[1] = num >>>
|
|
218
|
-
extra[2] = num >>>
|
|
219
|
-
extra[3] = gen &
|
|
220
|
-
extra[4] = gen >>>
|
|
195
|
+
extra[0] = num & BYTE_MASK;
|
|
196
|
+
extra[1] = num >>> BITS_PER_BYTE & BYTE_MASK;
|
|
197
|
+
extra[2] = num >>> UINT32_BYTE_2_SHIFT & BYTE_MASK;
|
|
198
|
+
extra[3] = gen & BYTE_MASK;
|
|
199
|
+
extra[4] = gen >>> BITS_PER_BYTE & BYTE_MASK;
|
|
221
200
|
return md5(concatBytes(method === "aes" ? [
|
|
222
201
|
fileKey,
|
|
223
202
|
extra,
|