reamkit 1.19.0 → 1.20.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/dist/esm/core/converter/ream.d.ts +4 -2
- package/dist/esm/core/converter/ream.js +8 -14
- package/dist/esm/core/crypto/offcrypto.d.ts +16 -0
- package/dist/esm/core/crypto/offcrypto.js +222 -0
- package/dist/esm/{pdf-reader/crypto.d.ts → core/crypto/primitives.d.ts} +10 -0
- package/dist/esm/{pdf-reader/crypto.js → core/crypto/primitives.js} +176 -45
- package/dist/esm/pdf-reader/decrypt.js +1 -1
- package/package.json +1 -1
|
@@ -14,8 +14,10 @@ export interface ReamParseOptions {
|
|
|
14
14
|
/** Reader registry override; defaults to the built-in docx + xlsx readers. */
|
|
15
15
|
readonly readers?: ReadonlyArray<DocumentReader<SourceDoc>>;
|
|
16
16
|
/**
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
17
|
+
* The password an encrypted source is opened with: a PDF's user password
|
|
18
|
+
* (ISO 32000 §7.6) or an OOXML package's (ECMA-376 §2.3, MS-OFFCRYPTO).
|
|
19
|
+
* Defaults to the empty string, which opens a PDF's common permissions-only
|
|
20
|
+
* encryption (EP14); an encrypted OOXML package always names a password.
|
|
19
21
|
*/
|
|
20
22
|
readonly password?: string;
|
|
21
23
|
}
|
|
@@ -13,8 +13,8 @@ import { writeXlsx } from "../../excel/xlsx-writer.js";
|
|
|
13
13
|
import { writeHtml } from "../../html/html-writer.js";
|
|
14
14
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
15
15
|
import { resolveDocxAutoFonts } from "../../word/docx-to-pdf.js";
|
|
16
|
-
import { isCfb, openCfb } from "../ole/cfb.js";
|
|
17
16
|
import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
|
|
17
|
+
import { decryptPackage, isEncryptedPackage } from "../crypto/offcrypto.js";
|
|
18
18
|
//#region src/core/converter/ream.ts
|
|
19
19
|
/**
|
|
20
20
|
* The point size Excel's column-width unit is quoted at — its default theme
|
|
@@ -77,12 +77,14 @@ var Ream = class Ream {
|
|
|
77
77
|
*/
|
|
78
78
|
static parse(bytes, options = {}) {
|
|
79
79
|
const readers = options.readers ?? DEFAULT_READERS;
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
if (
|
|
83
|
-
|
|
80
|
+
let source = bytes;
|
|
81
|
+
if (isEncryptedPackage(bytes)) {
|
|
82
|
+
if (options.password === void 0) throw new Error("Password-protected OOXML document (ECMA-376 §2.3 EncryptedPackage) — pass the password as `password`");
|
|
83
|
+
source = decryptPackage(bytes, options.password);
|
|
84
84
|
}
|
|
85
|
-
const
|
|
85
|
+
const reader = readers.find((r) => r.sniff(source));
|
|
86
|
+
if (!reader) throw new Error(`Unrecognized document format (readers: ${readers.map((r) => r.id).join(", ")})`);
|
|
87
|
+
const { doc, losses } = reader.read(source, { password: options.password });
|
|
86
88
|
const sheet = doc.kind === "sheet" ? doc : void 0;
|
|
87
89
|
return new Ream(toFlowDoc(doc), sheet, losses, bytes, reader.id);
|
|
88
90
|
}
|
|
@@ -232,13 +234,5 @@ var Ream = class Ream {
|
|
|
232
234
|
if (options.strict && losses.length > 0) throw new ConversionLossError(losses[0]);
|
|
233
235
|
}
|
|
234
236
|
};
|
|
235
|
-
function isEncryptedOoxml(bytes) {
|
|
236
|
-
if (!isCfb(bytes)) return false;
|
|
237
|
-
try {
|
|
238
|
-
return openCfb(bytes).hasStream("EncryptedPackage");
|
|
239
|
-
} catch {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
237
|
//#endregion
|
|
244
238
|
export { Ream };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Thrown when the bytes are encrypted and the password does not open them. */
|
|
2
|
+
export declare class WrongPasswordError extends Error {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
/** Whether the bytes are an OOXML package encrypted per ECMA-376 §2.3. */
|
|
6
|
+
export declare function isEncryptedPackage(bytes: Uint8Array): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Decrypt an encrypted OOXML package into the plain OPC zip inside it.
|
|
9
|
+
*
|
|
10
|
+
* @param bytes The compound file (`EncryptionInfo` + `EncryptedPackage`).
|
|
11
|
+
* @param password The password to open it with.
|
|
12
|
+
* @returns The decrypted package bytes — an ordinary `.docx`/`.xlsx` zip.
|
|
13
|
+
* @throws WrongPasswordError when the password fails the document's own
|
|
14
|
+
* verifier, and Error when the encryption is one this reader does not know.
|
|
15
|
+
*/
|
|
16
|
+
export declare function decryptPackage(bytes: Uint8Array, password: string): Uint8Array;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { openCfb } from "../ole/cfb.js";
|
|
2
|
+
import { aesCbcDecrypt, aesEcbDecrypt, sha1, sha256, sha384, sha512 } from "./primitives.js";
|
|
3
|
+
//#region src/core/crypto/offcrypto.ts
|
|
4
|
+
/** Thrown when the bytes are encrypted and the password does not open them. */
|
|
5
|
+
var WrongPasswordError = class extends Error {
|
|
6
|
+
constructor(message = "Wrong password for this document") {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "WrongPasswordError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/** Whether the bytes are an OOXML package encrypted per ECMA-376 §2.3. */
|
|
12
|
+
function isEncryptedPackage(bytes) {
|
|
13
|
+
try {
|
|
14
|
+
const cfb = openCfb(bytes);
|
|
15
|
+
return cfb.hasStream("EncryptedPackage") && cfb.hasStream("EncryptionInfo");
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Decrypt an encrypted OOXML package into the plain OPC zip inside it.
|
|
22
|
+
*
|
|
23
|
+
* @param bytes The compound file (`EncryptionInfo` + `EncryptedPackage`).
|
|
24
|
+
* @param password The password to open it with.
|
|
25
|
+
* @returns The decrypted package bytes — an ordinary `.docx`/`.xlsx` zip.
|
|
26
|
+
* @throws WrongPasswordError when the password fails the document's own
|
|
27
|
+
* verifier, and Error when the encryption is one this reader does not know.
|
|
28
|
+
*/
|
|
29
|
+
function decryptPackage(bytes, password) {
|
|
30
|
+
const cfb = openCfb(bytes);
|
|
31
|
+
const info = cfb.readStream("EncryptionInfo");
|
|
32
|
+
const payload = cfb.readStream("EncryptedPackage");
|
|
33
|
+
if (!info || !payload) throw new Error("Not an encrypted OOXML package");
|
|
34
|
+
const v = new DataView(info.buffer, info.byteOffset, info.byteLength);
|
|
35
|
+
const major = v.getUint16(0, true);
|
|
36
|
+
const minor = v.getUint16(2, true);
|
|
37
|
+
if (major === 4 && minor === 4) return agile(info, payload, password);
|
|
38
|
+
if ((major === 3 || major === 4) && minor === 2) return standard(info, payload, password);
|
|
39
|
+
throw new Error(`Unsupported OOXML encryption (version ${String(major)}.${String(minor)})`);
|
|
40
|
+
}
|
|
41
|
+
function packageSize(payload) {
|
|
42
|
+
const v = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
43
|
+
const lo = v.getUint32(0, true);
|
|
44
|
+
return v.getUint32(4, true) * 4294967296 + lo;
|
|
45
|
+
}
|
|
46
|
+
function standard(info, payload, password) {
|
|
47
|
+
const v = new DataView(info.buffer, info.byteOffset, info.byteLength);
|
|
48
|
+
const headerSize = v.getUint32(8, true);
|
|
49
|
+
const algId = v.getUint32(20, true);
|
|
50
|
+
const keyBits = v.getUint32(28, true);
|
|
51
|
+
let at = 12 + headerSize;
|
|
52
|
+
const saltSize = v.getUint32(at, true);
|
|
53
|
+
at += 4;
|
|
54
|
+
const salt = info.subarray(at, at + saltSize);
|
|
55
|
+
at += saltSize;
|
|
56
|
+
const encryptedVerifier = info.subarray(at, at + 16);
|
|
57
|
+
at += 16;
|
|
58
|
+
const verifierHashSize = v.getUint32(at, true);
|
|
59
|
+
at += 4;
|
|
60
|
+
const encryptedVerifierHash = info.subarray(at, at + (verifierHashSize <= 20 ? 32 : 32));
|
|
61
|
+
if (algId !== 0 && (algId < 26126 || algId > 26128)) throw new Error(`Unsupported OOXML cipher (algId 0x${algId.toString(16)})`);
|
|
62
|
+
const key = standardKey(password, salt, Math.max(16, Math.floor((keyBits || 128) / 8)));
|
|
63
|
+
const verifier = aesEcbDecrypt(key, encryptedVerifier);
|
|
64
|
+
const expected = aesEcbDecrypt(key, encryptedVerifierHash).subarray(0, 20);
|
|
65
|
+
if (!sameBytes(sha1(verifier), expected)) throw new WrongPasswordError();
|
|
66
|
+
const plain = aesEcbDecrypt(key, payload.subarray(8));
|
|
67
|
+
return plain.subarray(0, Math.min(packageSize(payload), plain.length));
|
|
68
|
+
}
|
|
69
|
+
function standardKey(password, salt, keyLength) {
|
|
70
|
+
let h = sha1(concat(salt, utf16le(password)));
|
|
71
|
+
const counter = new Uint8Array(4);
|
|
72
|
+
for (let i = 0; i < 5e4; i++) {
|
|
73
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
74
|
+
h = sha1(concat(counter, h));
|
|
75
|
+
}
|
|
76
|
+
h = sha1(concat(h, new Uint8Array(4)));
|
|
77
|
+
return concat(sha1(padded(h, 54)), sha1(padded(h, 92))).subarray(0, keyLength);
|
|
78
|
+
}
|
|
79
|
+
function padded(h, byte) {
|
|
80
|
+
const buf = new Uint8Array(64).fill(byte);
|
|
81
|
+
for (let i = 0; i < h.length && i < 64; i++) buf[i] = buf[i] ^ h[i];
|
|
82
|
+
return buf;
|
|
83
|
+
}
|
|
84
|
+
var BLOCK_VERIFIER_INPUT = Uint8Array.of(254, 167, 210, 118, 59, 75, 158, 121);
|
|
85
|
+
var BLOCK_VERIFIER_VALUE = Uint8Array.of(215, 170, 15, 109, 48, 97, 52, 78);
|
|
86
|
+
var BLOCK_KEY_VALUE = Uint8Array.of(20, 110, 11, 231, 171, 172, 208, 214);
|
|
87
|
+
var SEGMENT = 4096;
|
|
88
|
+
function agile(info, payload, password) {
|
|
89
|
+
const xml = new TextDecoder().decode(info.subarray(8));
|
|
90
|
+
const keyData = agileKeyData(attrsOf(xml, "keyData"));
|
|
91
|
+
const encAttrs = passwordKeyEncryptor(xml);
|
|
92
|
+
const enc = agileKeyData(encAttrs);
|
|
93
|
+
const spinCount = Number(encAttrs["spinCount"] ?? "0");
|
|
94
|
+
const encryptedKeyValue = b64(encAttrs["encryptedKeyValue"] ?? "");
|
|
95
|
+
const encryptedVerifierHashInput = b64(encAttrs["encryptedVerifierHashInput"] ?? "");
|
|
96
|
+
const encryptedVerifierHashValue = b64(encAttrs["encryptedVerifierHashValue"] ?? "");
|
|
97
|
+
const spun = agileSpin(password, enc, spinCount);
|
|
98
|
+
const keyFor = (block) => agileBlockKey(spun, enc, block);
|
|
99
|
+
const verifier = decryptAgile(keyFor(BLOCK_VERIFIER_INPUT), enc.saltValue, encryptedVerifierHashInput, enc.cbc);
|
|
100
|
+
const expected = decryptAgile(keyFor(BLOCK_VERIFIER_VALUE), enc.saltValue, encryptedVerifierHashValue, enc.cbc);
|
|
101
|
+
const actual = enc.hash(verifier.subarray(0, enc.saltValue.length));
|
|
102
|
+
if (!sameBytes(actual, expected.subarray(0, actual.length))) throw new WrongPasswordError();
|
|
103
|
+
const packageKey = decryptAgile(keyFor(BLOCK_KEY_VALUE), enc.saltValue, encryptedKeyValue, enc.cbc).subarray(0, keyData.keyBits / 8);
|
|
104
|
+
const cipher = payload.subarray(8);
|
|
105
|
+
const out = new Uint8Array(cipher.length);
|
|
106
|
+
const counter = new Uint8Array(4);
|
|
107
|
+
for (let i = 0; i * SEGMENT < cipher.length; i++) {
|
|
108
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
109
|
+
const iv = keyData.hash(concat(keyData.saltValue, counter)).subarray(0, keyData.blockSize || 16);
|
|
110
|
+
const block = cipher.subarray(i * SEGMENT, Math.min((i + 1) * SEGMENT, cipher.length));
|
|
111
|
+
out.set(decryptAgile(packageKey, iv, block, keyData.cbc), i * SEGMENT);
|
|
112
|
+
}
|
|
113
|
+
return out.subarray(0, Math.min(packageSize(payload), out.length));
|
|
114
|
+
}
|
|
115
|
+
function agileSpin(password, enc, spinCount) {
|
|
116
|
+
let h = enc.hash(concat(enc.saltValue, utf16le(password)));
|
|
117
|
+
const counter = new Uint8Array(4);
|
|
118
|
+
for (let i = 0; i < spinCount; i++) {
|
|
119
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
120
|
+
h = enc.hash(concat(counter, h));
|
|
121
|
+
}
|
|
122
|
+
return h;
|
|
123
|
+
}
|
|
124
|
+
function agileBlockKey(spun, enc, block) {
|
|
125
|
+
const keyLength = enc.keyBits / 8;
|
|
126
|
+
const derived = enc.hash(concat(spun, block));
|
|
127
|
+
if (derived.length >= keyLength) return derived.subarray(0, keyLength);
|
|
128
|
+
const out = new Uint8Array(keyLength).fill(54);
|
|
129
|
+
out.set(derived);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function decryptAgile(key, iv, data, cbc) {
|
|
133
|
+
if (!cbc) return aesEcbDecrypt(key, data);
|
|
134
|
+
return aesCbcDecrypt(key, iv.length >= 16 ? iv.subarray(0, 16) : concat(iv, new Uint8Array(16 - iv.length)), data, false);
|
|
135
|
+
}
|
|
136
|
+
var HASHES = new Map([
|
|
137
|
+
["sha1", sha1],
|
|
138
|
+
["sha-1", sha1],
|
|
139
|
+
["sha256", sha256],
|
|
140
|
+
["sha-256", sha256],
|
|
141
|
+
["sha384", sha384],
|
|
142
|
+
["sha-384", sha384],
|
|
143
|
+
["sha512", sha512],
|
|
144
|
+
["sha-512", sha512]
|
|
145
|
+
]);
|
|
146
|
+
function agileKeyData(attrs) {
|
|
147
|
+
const name = (attrs["hashAlgorithm"] ?? "sha512").toLowerCase();
|
|
148
|
+
const hash = HASHES.get(name);
|
|
149
|
+
if (!hash) throw new Error(`Unsupported OOXML hash (${name})`);
|
|
150
|
+
const cipher = (attrs["cipherAlgorithm"] ?? "AES").toUpperCase();
|
|
151
|
+
if (!cipher.startsWith("AES")) throw new Error(`Unsupported OOXML cipher (${cipher})`);
|
|
152
|
+
return {
|
|
153
|
+
saltValue: b64(attrs["saltValue"] ?? ""),
|
|
154
|
+
blockSize: Number(attrs["blockSize"] ?? "16"),
|
|
155
|
+
keyBits: Number(attrs["keyBits"] ?? "256"),
|
|
156
|
+
hash,
|
|
157
|
+
cbc: (attrs["cipherChaining"] ?? "ChainingModeCBC") !== "ChainingModeECB"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function passwordKeyEncryptor(xml) {
|
|
161
|
+
const re = /<[A-Za-z0-9]*:?encryptedKey\b([^>]*)\/?>/gu;
|
|
162
|
+
let m = re.exec(xml);
|
|
163
|
+
while (m) {
|
|
164
|
+
const attrs = parseAttrs(m[1] ?? "");
|
|
165
|
+
if (attrs["spinCount"] !== void 0) return attrs;
|
|
166
|
+
m = re.exec(xml);
|
|
167
|
+
}
|
|
168
|
+
throw new Error("No password key encryptor in the encryption descriptor");
|
|
169
|
+
}
|
|
170
|
+
function attrsOf(xml, tag) {
|
|
171
|
+
const m = new RegExp(`<[A-Za-z0-9]*:?${tag}\\b([^>]*)/?>`, "u").exec(xml);
|
|
172
|
+
return m ? parseAttrs(m[1] ?? "") : {};
|
|
173
|
+
}
|
|
174
|
+
function parseAttrs(raw) {
|
|
175
|
+
const out = {};
|
|
176
|
+
const re = /([A-Za-z0-9_:]+)\s*=\s*"([^"]*)"/gu;
|
|
177
|
+
let m = re.exec(raw);
|
|
178
|
+
while (m) {
|
|
179
|
+
out[m[1].replace(/^.*:/u, "")] = m[2];
|
|
180
|
+
m = re.exec(raw);
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function utf16le(s) {
|
|
185
|
+
const out = new Uint8Array(s.length * 2);
|
|
186
|
+
const v = new DataView(out.buffer);
|
|
187
|
+
for (let i = 0; i < s.length; i++) v.setUint16(i * 2, s.charCodeAt(i), true);
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
function concat(a, b) {
|
|
191
|
+
const out = new Uint8Array(a.length + b.length);
|
|
192
|
+
out.set(a);
|
|
193
|
+
out.set(b, a.length);
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function sameBytes(a, b) {
|
|
197
|
+
if (a.length !== b.length) return false;
|
|
198
|
+
let diff = 0;
|
|
199
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
200
|
+
return diff === 0;
|
|
201
|
+
}
|
|
202
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
203
|
+
function b64(s) {
|
|
204
|
+
const clean = s.replace(/[^A-Za-z0-9+/]/gu, "");
|
|
205
|
+
const out = new Uint8Array(Math.floor(clean.length * 3 / 4));
|
|
206
|
+
let acc = 0;
|
|
207
|
+
let bits = 0;
|
|
208
|
+
let at = 0;
|
|
209
|
+
for (const ch of clean) {
|
|
210
|
+
const v = B64.indexOf(ch);
|
|
211
|
+
if (v < 0) continue;
|
|
212
|
+
acc = acc << 6 | v;
|
|
213
|
+
bits += 6;
|
|
214
|
+
if (bits >= 8) {
|
|
215
|
+
bits -= 8;
|
|
216
|
+
out[at++] = acc >> bits & 255;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out.subarray(0, at);
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
export { decryptPackage, isEncryptedPackage };
|
|
@@ -5,8 +5,18 @@
|
|
|
5
5
|
export declare function rc4(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
6
6
|
/** MD5 digest (RFC 1321) → 16 bytes; the key-derivation hash for V1/V2/V4. */
|
|
7
7
|
export declare function md5(input: Uint8Array): Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* SHA-1. Used by MS-OFFCRYPTO's standard encryption for its key derivation and
|
|
10
|
+
* password verifier, and by agile encryption when the descriptor names it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sha1(input: Uint8Array): Uint8Array;
|
|
8
13
|
export declare function sha256(input: Uint8Array): Uint8Array;
|
|
9
14
|
export declare function sha512(input: Uint8Array): Uint8Array;
|
|
10
15
|
export declare function sha384(input: Uint8Array): Uint8Array;
|
|
11
16
|
export declare function aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array, stripPad: boolean): Uint8Array;
|
|
17
|
+
/**
|
|
18
|
+
* AES-ECB decrypt — every block on its own key, no chaining. MS-OFFCRYPTO's
|
|
19
|
+
* standard encryption uses it for both the verifier and the package.
|
|
20
|
+
*/
|
|
21
|
+
export declare function aesEcbDecrypt(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
12
22
|
export declare function aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Uint8Array;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/core/crypto/primitives.ts
|
|
2
2
|
/**
|
|
3
3
|
* RC4 stream cipher (§7.6.2). Symmetric, so the same call both encrypts and
|
|
4
4
|
* decrypts; used by the legacy V1/V2/V4 Standard security handlers.
|
|
@@ -217,6 +217,58 @@ var SHA256_K = [
|
|
|
217
217
|
3204031479,
|
|
218
218
|
3329325298
|
|
219
219
|
];
|
|
220
|
+
/**
|
|
221
|
+
* SHA-1. Used by MS-OFFCRYPTO's standard encryption for its key derivation and
|
|
222
|
+
* password verifier, and by agile encryption when the descriptor names it.
|
|
223
|
+
*/
|
|
224
|
+
function sha1(input) {
|
|
225
|
+
const ml = input.length * 8;
|
|
226
|
+
const withPad = new Uint8Array((input.length + 8 >> 6 << 6) + 64);
|
|
227
|
+
withPad.set(input);
|
|
228
|
+
withPad[input.length] = 128;
|
|
229
|
+
const view = new DataView(withPad.buffer);
|
|
230
|
+
view.setUint32(withPad.length - 4, ml >>> 0, false);
|
|
231
|
+
view.setUint32(withPad.length - 8, Math.floor(ml / 4294967296), false);
|
|
232
|
+
let h0 = 1732584193;
|
|
233
|
+
let h1 = 4023233417;
|
|
234
|
+
let h2 = 2562383102;
|
|
235
|
+
let h3 = 271733878;
|
|
236
|
+
let h4 = 3285377520;
|
|
237
|
+
const w = new Uint32Array(80);
|
|
238
|
+
for (let off = 0; off < withPad.length; off += 64) {
|
|
239
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4, false);
|
|
240
|
+
for (let i = 16; i < 80; i++) {
|
|
241
|
+
const x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
|
|
242
|
+
w[i] = x << 1 | x >>> 31;
|
|
243
|
+
}
|
|
244
|
+
let a = h0;
|
|
245
|
+
let b = h1;
|
|
246
|
+
let c = h2;
|
|
247
|
+
let d = h3;
|
|
248
|
+
let e = h4;
|
|
249
|
+
for (let i = 0; i < 80; i++) {
|
|
250
|
+
const [f, k] = i < 20 ? [b & c | ~b & d, 1518500249] : i < 40 ? [b ^ c ^ d, 1859775393] : i < 60 ? [b & c | b & d | c & d, 2400959708] : [b ^ c ^ d, 3395469782];
|
|
251
|
+
const t = (a << 5 | a >>> 27) + f + e + k + w[i] >>> 0;
|
|
252
|
+
e = d;
|
|
253
|
+
d = c;
|
|
254
|
+
c = (b << 30 | b >>> 2) >>> 0;
|
|
255
|
+
b = a;
|
|
256
|
+
a = t;
|
|
257
|
+
}
|
|
258
|
+
h0 = h0 + a >>> 0;
|
|
259
|
+
h1 = h1 + b >>> 0;
|
|
260
|
+
h2 = h2 + c >>> 0;
|
|
261
|
+
h3 = h3 + d >>> 0;
|
|
262
|
+
h4 = h4 + e >>> 0;
|
|
263
|
+
}
|
|
264
|
+
const out = new Uint8Array(20);
|
|
265
|
+
new DataView(out.buffer).setUint32(0, h0, false);
|
|
266
|
+
new DataView(out.buffer).setUint32(4, h1, false);
|
|
267
|
+
new DataView(out.buffer).setUint32(8, h2, false);
|
|
268
|
+
new DataView(out.buffer).setUint32(12, h3, false);
|
|
269
|
+
new DataView(out.buffer).setUint32(16, h4, false);
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
220
272
|
function sha256(input) {
|
|
221
273
|
const h = [
|
|
222
274
|
1779033703,
|
|
@@ -275,7 +327,6 @@ function sha256(input) {
|
|
|
275
327
|
for (let i = 0; i < 8; i++) new DataView(out.buffer).setUint32(i * 4, h[i] >>> 0);
|
|
276
328
|
return out;
|
|
277
329
|
}
|
|
278
|
-
var MASK64 = (1n << 64n) - 1n;
|
|
279
330
|
var SHA512_K = [
|
|
280
331
|
4794697086780616226n,
|
|
281
332
|
8158064640168781261n,
|
|
@@ -358,62 +409,130 @@ var SHA512_K = [
|
|
|
358
409
|
6902733635092675308n,
|
|
359
410
|
7801388544844847127n
|
|
360
411
|
];
|
|
412
|
+
var SHA512_KH = new Int32Array(80);
|
|
413
|
+
var SHA512_KL = new Int32Array(80);
|
|
414
|
+
for (let i = 0; i < 80; i++) {
|
|
415
|
+
SHA512_KH[i] = Number(SHA512_K[i] >> 32n & 4294967295n) | 0;
|
|
416
|
+
SHA512_KL[i] = Number(SHA512_K[i] & 4294967295n) | 0;
|
|
417
|
+
}
|
|
361
418
|
function sha512Core(iv, input, outLen) {
|
|
362
|
-
const
|
|
363
|
-
const
|
|
419
|
+
const hh = new Int32Array(8);
|
|
420
|
+
const hl = new Int32Array(8);
|
|
421
|
+
for (let i = 0; i < 8; i++) {
|
|
422
|
+
hh[i] = Number(iv[i] >> 32n & 4294967295n) | 0;
|
|
423
|
+
hl[i] = Number(iv[i] & 4294967295n) | 0;
|
|
424
|
+
}
|
|
364
425
|
const len = input.length;
|
|
365
426
|
const blocks = (len + 16 >> 7) + 1;
|
|
366
427
|
const msg = new Uint8Array(blocks * 128);
|
|
367
428
|
msg.set(input);
|
|
368
429
|
msg[len] = 128;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const
|
|
430
|
+
const view = new DataView(msg.buffer);
|
|
431
|
+
const bits = len * 8;
|
|
432
|
+
view.setUint32(msg.length - 4, bits >>> 0, false);
|
|
433
|
+
view.setUint32(msg.length - 8, Math.floor(bits / 4294967296), false);
|
|
434
|
+
const wh = new Int32Array(80);
|
|
435
|
+
const wl = new Int32Array(80);
|
|
375
436
|
for (let off = 0; off < msg.length; off += 128) {
|
|
376
437
|
for (let i = 0; i < 16; i++) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
w[i] = v;
|
|
438
|
+
wh[i] = view.getInt32(off + i * 8, false);
|
|
439
|
+
wl[i] = view.getInt32(off + i * 8 + 4, false);
|
|
380
440
|
}
|
|
381
441
|
for (let i = 16; i < 80; i++) {
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
442
|
+
const x15h = wh[i - 15];
|
|
443
|
+
const x15l = wl[i - 15];
|
|
444
|
+
const s0h = (x15h >>> 1 | x15l << 31) ^ (x15h >>> 8 | x15l << 24) ^ x15h >>> 7;
|
|
445
|
+
const s0l = (x15l >>> 1 | x15h << 31) ^ (x15l >>> 8 | x15h << 24) ^ (x15l >>> 7 | x15h << 25);
|
|
446
|
+
const x2h = wh[i - 2];
|
|
447
|
+
const x2l = wl[i - 2];
|
|
448
|
+
const s1h = (x2h >>> 19 | x2l << 13) ^ (x2l >>> 29 | x2h << 3) ^ x2h >>> 6;
|
|
449
|
+
const s1l = (x2l >>> 19 | x2h << 13) ^ (x2h >>> 29 | x2l << 3) ^ (x2l >>> 6 | x2h << 26);
|
|
450
|
+
let lo = (wl[i - 16] >>> 0) + (s0l >>> 0);
|
|
451
|
+
let hi = (wh[i - 16] >>> 0) + (s0h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
452
|
+
lo = (lo >>> 0) + (wl[i - 7] >>> 0);
|
|
453
|
+
hi = hi + (wh[i - 7] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
454
|
+
lo = (lo >>> 0) + (s1l >>> 0);
|
|
455
|
+
hi = hi + (s1h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
456
|
+
wh[i] = hi | 0;
|
|
457
|
+
wl[i] = lo | 0;
|
|
385
458
|
}
|
|
386
|
-
let
|
|
459
|
+
let ah = hh[0];
|
|
460
|
+
let al = hl[0];
|
|
461
|
+
let bh = hh[1];
|
|
462
|
+
let bl = hl[1];
|
|
463
|
+
let ch = hh[2];
|
|
464
|
+
let cl = hl[2];
|
|
465
|
+
let dh = hh[3];
|
|
466
|
+
let dl = hl[3];
|
|
467
|
+
let eh = hh[4];
|
|
468
|
+
let el = hl[4];
|
|
469
|
+
let fh = hh[5];
|
|
470
|
+
let fl = hl[5];
|
|
471
|
+
let gh = hh[6];
|
|
472
|
+
let gl = hl[6];
|
|
473
|
+
let xh = hh[7];
|
|
474
|
+
let xl = hl[7];
|
|
387
475
|
for (let i = 0; i < 80; i++) {
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
476
|
+
const S1h = (eh >>> 14 | el << 18) ^ (eh >>> 18 | el << 14) ^ (el >>> 9 | eh << 23);
|
|
477
|
+
const S1l = (el >>> 14 | eh << 18) ^ (el >>> 18 | eh << 14) ^ (eh >>> 9 | el << 23);
|
|
478
|
+
const chh = eh & fh ^ ~eh & gh;
|
|
479
|
+
const chl = el & fl ^ ~el & gl;
|
|
480
|
+
const S0h = (ah >>> 28 | al << 4) ^ (al >>> 2 | ah << 30) ^ (al >>> 7 | ah << 25);
|
|
481
|
+
const S0l = (al >>> 28 | ah << 4) ^ (ah >>> 2 | al << 30) ^ (ah >>> 7 | al << 25);
|
|
482
|
+
const majh = ah & bh ^ ah & ch ^ bh & ch;
|
|
483
|
+
const majl = al & bl ^ al & cl ^ bl & cl;
|
|
484
|
+
let lo = (xl >>> 0) + (S1l >>> 0);
|
|
485
|
+
let hi = (xh >>> 0) + (S1h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
486
|
+
lo = (lo >>> 0) + (chl >>> 0);
|
|
487
|
+
hi = hi + (chh >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
488
|
+
lo = (lo >>> 0) + (SHA512_KL[i] >>> 0);
|
|
489
|
+
hi = hi + (SHA512_KH[i] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
490
|
+
lo = (lo >>> 0) + (wl[i] >>> 0);
|
|
491
|
+
hi = hi + (wh[i] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
492
|
+
const t1h = hi | 0;
|
|
493
|
+
const t1l = lo | 0;
|
|
494
|
+
const lo2 = (S0l >>> 0) + (majl >>> 0);
|
|
495
|
+
const t2h = (S0h >>> 0) + (majh >>> 0) + (lo2 > 4294967295 ? 1 : 0) | 0;
|
|
496
|
+
const t2l = lo2 | 0;
|
|
497
|
+
xh = gh;
|
|
498
|
+
xl = gl;
|
|
499
|
+
gh = fh;
|
|
500
|
+
gl = fl;
|
|
501
|
+
fh = eh;
|
|
502
|
+
fl = el;
|
|
503
|
+
lo = (dl >>> 0) + (t1l >>> 0);
|
|
504
|
+
eh = (dh >>> 0) + (t1h >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
505
|
+
el = lo | 0;
|
|
506
|
+
dh = ch;
|
|
507
|
+
dl = cl;
|
|
508
|
+
ch = bh;
|
|
509
|
+
cl = bl;
|
|
510
|
+
bh = ah;
|
|
511
|
+
bl = al;
|
|
512
|
+
lo = (t1l >>> 0) + (t2l >>> 0);
|
|
513
|
+
ah = (t1h >>> 0) + (t2h >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
514
|
+
al = lo | 0;
|
|
400
515
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
516
|
+
const add = (i, addH, addL) => {
|
|
517
|
+
const lo = (hl[i] >>> 0) + (addL >>> 0);
|
|
518
|
+
hh[i] = (hh[i] >>> 0) + (addH >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
519
|
+
hl[i] = lo | 0;
|
|
520
|
+
};
|
|
521
|
+
add(0, ah, al);
|
|
522
|
+
add(1, bh, bl);
|
|
523
|
+
add(2, ch, cl);
|
|
524
|
+
add(3, dh, dl);
|
|
525
|
+
add(4, eh, el);
|
|
526
|
+
add(5, fh, fl);
|
|
527
|
+
add(6, gh, gl);
|
|
528
|
+
add(7, xh, xl);
|
|
529
|
+
}
|
|
530
|
+
const out = new Uint8Array(64);
|
|
531
|
+
const ov = new DataView(out.buffer);
|
|
532
|
+
for (let i = 0; i < 8; i++) {
|
|
533
|
+
ov.setInt32(i * 8, hh[i], false);
|
|
534
|
+
ov.setInt32(i * 8 + 4, hl[i], false);
|
|
409
535
|
}
|
|
410
|
-
const out = new Uint8Array(h.length * 8);
|
|
411
|
-
h.forEach((v, i) => {
|
|
412
|
-
for (let b = 7; b >= 0; b--) {
|
|
413
|
-
out[i * 8 + b] = Number(v & 255n);
|
|
414
|
-
v >>= 8n;
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
536
|
return out.slice(0, outLen);
|
|
418
537
|
}
|
|
419
538
|
function sha512(input) {
|
|
@@ -559,6 +678,18 @@ function aesCbcDecrypt(key, iv, data, stripPad) {
|
|
|
559
678
|
const pad = out[out.length - 1];
|
|
560
679
|
return pad >= 1 && pad <= 16 && pad <= out.length ? out.subarray(0, out.length - pad) : out;
|
|
561
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* AES-ECB decrypt — every block on its own key, no chaining. MS-OFFCRYPTO's
|
|
683
|
+
* standard encryption uses it for both the verifier and the package.
|
|
684
|
+
*/
|
|
685
|
+
function aesEcbDecrypt(key, data) {
|
|
686
|
+
const w = expandKey(key);
|
|
687
|
+
const nr = key.length / 4 + 6;
|
|
688
|
+
const blocks = Math.floor(data.length / 16);
|
|
689
|
+
const out = new Uint8Array(blocks * 16);
|
|
690
|
+
for (let i = 0; i < blocks; i++) decryptBlock(data.subarray(i * 16, i * 16 + 16), w, nr, out, i * 16);
|
|
691
|
+
return out;
|
|
692
|
+
}
|
|
562
693
|
function aesCbcEncrypt(key, iv, data) {
|
|
563
694
|
const w = expandKey(key);
|
|
564
695
|
const nr = key.length / 4 + 6;
|
|
@@ -611,4 +742,4 @@ function mixColumns(s) {
|
|
|
611
742
|
}
|
|
612
743
|
}
|
|
613
744
|
//#endregion
|
|
614
|
-
export { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 };
|
|
745
|
+
export { aesCbcDecrypt, aesCbcEncrypt, aesEcbDecrypt, md5, rc4, sha1, sha256, sha384, sha512 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
|
-
import { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 } from "
|
|
2
|
+
import { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 } from "../core/crypto/primitives.js";
|
|
3
3
|
//#region src/pdf-reader/decrypt.ts
|
|
4
4
|
var PAD = Uint8Array.from([
|
|
5
5
|
40,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|