archive-codec 1.8.0 → 1.9.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white)](https://github.com/ExaDev/documents.js/tree/main/packages/archive-codec) [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/archive-codec) [![npm version](https://img.shields.io/npm/v/archive-codec)](https://www.npmjs.com/package/archive-codec) [![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/documents.js/ci.yml?branch=main)](https://github.com/ExaDev/documents.js/actions)
4
4
 
5
- > ZIP-in-ZIP recursive walking under depth and cumulative decompressed-size guards, classic OLE compound-file ([MS-CFB]) reading and writing, [MS-OLEPS] Property Set Stream reading and writing, and the RC4/MD5 primitives the legacy OLE-based binary formats share for their own password-to-open encryption — zero document-format knowledge, the archive and container utility package for the [documents.js family](../../README.md). Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
5
+ > ZIP-in-ZIP recursive walking under depth and cumulative decompressed-size guards, classic OLE compound-file ([MS-CFB]) reading and writing, [MS-OLEPS] Property Set Stream reading and writing, and the RC4/MD5/SHA-1 primitives the legacy OLE-based binary formats use for their own password-to-open encryption — zero document-format knowledge, the archive and container utility package for the [documents.js family](../../README.md). Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
6
6
 
7
7
  Created for [documents.js#564](https://github.com/ExaDev/documents.js/issues/564): nothing in the ecosystem recursed into a nested archive. Most concretely, OOXML's embedded-object model — a docx/pptx carrying a genuinely separate ZIP blob at `word/embeddings/oleObject1.xlsx` — had no safe handling anywhere, and no package guarded against recursive-archive inputs at all (`byte-codec`'s 512 MiB per-stream inflate cap does not compose across recursion). A new sibling was chosen over extending `byte-codec` (whose charter is byte/image primitives, zero container-format knowledge) or doing it inline in `documents.js` (which would repeat the duplication `byte-codec`'s own extraction was meant to avoid). Its first family consumer is `ooxml.js`'s OLE embedded-object recovery — [documents.js#733](https://github.com/ExaDev/documents.js/issues/733) (pptx, `p:oleObj`) and [documents.js#734](https://github.com/ExaDev/documents.js/issues/734) (docx, `o:OLEObject`): an OLE payload part's bytes are checked through `isZipArchive` and, when they are a ZIP, decoded as a nested OOXML package behind this package's guarded walk — the bounded inflate that populates `document-schema.js`'s `ContentEmbeddedObject`/`ContentEmbeddedObjectBlock` (the same vocabulary odf.js embeds formula sub-documents through) with a genuinely recovered sub-document.
8
8
 
@@ -14,11 +14,13 @@ Created for [documents.js#564](https://github.com/ExaDev/documents.js/issues/564
14
14
 
15
15
  [documents.js#815](https://github.com/ExaDev/documents.js/issues/815), [#816](https://github.com/ExaDev/documents.js/issues/816), and [#817](https://github.com/ExaDev/documents.js/issues/817) also each named the same remaining gap: `doc-codec`, `xls-codec`, and `ppt-codec` all hard-coded document metadata (title, author, dates) to an empty object, because that metadata lives in a genuinely different structure from the one each format's own reader already parses -- a [MS-OLEPS] Property Set Stream, conventionally stored as a "\x05SummaryInformation" stream beside `WordDocument`/`Workbook`/`PowerPoint Document` in the identical [MS-CFB] container all three already read through this package. `oleps/read` and `oleps/write` are the generic property-set codec (the stream header, the PropertySet packet's dictionary, and VT_I2/VT_I4/VT_LPSTR/VT_LPWSTR/VT_FILETIME typed values), and `oleps/summary-information` is the SummaryInformation-specific mapping on top of it -- the same two-layer split `cfb/read.ts` and `cfb/ole-package.ts` already establish for the OLE Package stream, container structure below, one named stream's own field layout above.
16
16
 
17
- [documents.js#1108](https://github.com/ExaDev/documents.js/issues/1108) added the RC4/MD5 primitives `xls-codec` needs to decrypt a legacy-encrypted `.xls` workbook: the [MS-OFFCRYPTO] 2.3.6.1 "RC4 encryption header" scheme every BIFF8 `FilePass` record protected this way shares with the same header shape in `.doc` and `.ppt`. RC4 and MD5 are trivial and completely unavailable from any platform crypto API this package can portably reach (WebCrypto has never offered either; `node:crypto` dropped RC4 from its default provider with OpenSSL 3), so hand-writing them here — once, shared — is the only option that keeps `xls-codec`, `doc-codec`, and `ppt-codec` (already this package's only consumers) from each carrying their own copy. The newer "RC4 CryptoAPI encryption header" scheme (2.3.5.1, a different header shape and derivation) and the older XOR obfuscation scheme are explicitly out of scope for now — tracked separately in [documents.js#922](https://github.com/ExaDev/documents.js/issues/922).
17
+ [documents.js#1108](https://github.com/ExaDev/documents.js/issues/1108) added the RC4/MD5 primitives `xls-codec` needs to decrypt a legacy-encrypted `.xls` workbook: the [MS-OFFCRYPTO] 2.3.6.1 "RC4 encryption header" scheme every BIFF8 `FilePass` record protected this way shares with the same header shape in `.doc`. RC4 and MD5 are trivial and completely unavailable from any platform crypto API this package can portably reach (WebCrypto has never offered either; `node:crypto` dropped RC4 from its default provider with OpenSSL 3), so hand-writing them here — once, shared — is the only option that keeps `xls-codec` and `doc-codec` from each carrying their own copy. `.ppt` turned out NOT to share this scheme at all (see [documents.js#1116](https://github.com/ExaDev/documents.js/issues/1116) below) the older XOR obfuscation scheme remains out of scope, tracked in [documents.js#922](https://github.com/ExaDev/documents.js/issues/922).
18
18
 
19
- [documents.js#1113](https://github.com/ExaDev/documents.js/issues/1113) is the first of those two named future consumers to actually land: `doc-codec` decrypts an RC4-encrypted `.doc` given a password, wired against the same primitives `xls-codec` uses. The two codecs' own re-keying intervals genuinely differ (`OFFICE_RC4_BLOCK_SIZE` for `xls-codec`, `OFFICE_RC4_DOC_BLOCK_SIZE` for `doc-codec`), so `decryptOfficeRc4` gained a `blockSize` parameter rather than this package assuming one shared constant — see [Legacy Office encryption](#legacy-office-encryption).
19
+ [documents.js#1113](https://github.com/ExaDev/documents.js/issues/1113) is the first of two named future consumers from #1108 to actually land: `doc-codec` decrypts an RC4-encrypted `.doc` given a password, wired against the same primitives `xls-codec` uses. The two codecs' own re-keying intervals genuinely differ (`OFFICE_RC4_BLOCK_SIZE` for `xls-codec`, `OFFICE_RC4_DOC_BLOCK_SIZE` for `doc-codec`), so `decryptOfficeRc4` gained a `blockSize` parameter rather than this package assuming one shared constant — see [Legacy Office encryption](#legacy-office-encryption).
20
20
 
21
- Scope: **ZIP containers** (read and write over [`fflate`](https://github.com/101arrowz/fflate), recursive walking of ZIP-in-ZIP entries), **classic OLE compound files** (bounded [MS-CFB] reading and conformant [MS-CFB] writing, plus OLE Package stream reading and writing), **[MS-OLEPS] Property Set Streams** (generic read/write of a single-property-set stream, plus SummaryInformation's own title/subject/author/keywords/comments/created/last-saved/last-printed fields), and the **RC4/MD5 primitives** the legacy OLE-based binary formats share for password-to-open encryption. **tar and gzip, DocumentSummaryInformation's extended and user-defined property sets, writing VT_LPSTR (ANSI-codepage) string properties, RC4 CryptoAPI encryption, and XOR obfuscation are explicitly out of scope.**
21
+ [documents.js#1116](https://github.com/ExaDev/documents.js/issues/1116) is the second: `.ppt`'s own encryption turned out to be a genuinely different scheme, not `ppt-codec`'s eventual integration against #1108's own primitives as originally assumed. [MS-OFFCRYPTO] 2.3.5 "RC4 CryptoAPI Encryption" is SHA-1-based rather than MD5-based, derives its key with no intermediate-hash iteration, and re-keys per persist object rather than at a fixed byte interval different enough that it needed its own primitives (`crypto/sha1`, `crypto/office-rc4-cryptoapi`) rather than a parameter on the existing ones. See [RC4 CryptoAPI encryption](#rc4-cryptoapi-encryption-ppt).
22
+
23
+ Scope: **ZIP containers** (read and write over [`fflate`](https://github.com/101arrowz/fflate), recursive walking of ZIP-in-ZIP entries), **classic OLE compound files** (bounded [MS-CFB] reading and conformant [MS-CFB] writing, plus OLE Package stream reading and writing), **[MS-OLEPS] Property Set Streams** (generic read/write of a single-property-set stream, plus SummaryInformation's own title/subject/author/keywords/comments/created/last-saved/last-printed fields), and the **RC4/MD5/SHA-1 primitives** the legacy OLE-based binary formats use for password-to-open encryption — `.doc`/`.xls` share one MD5-based scheme, `.ppt` uses a genuinely different SHA-1-based one. **tar and gzip, DocumentSummaryInformation's extended and user-defined property sets, writing VT_LPSTR (ANSI-codepage) string properties, and XOR obfuscation are explicitly out of scope.**
22
24
 
23
25
  ## Getting started
24
26
 
@@ -48,22 +50,24 @@ import { walkArchive } from "archive-codec/zip/walk";
48
50
 
49
51
  The smoke suite (`test/smoke.test.mjs`) is the guard on that advertisement: it loads each module below from the built `dist/` in both module systems, so a build config that stops serving an advertised subpath fails the suite — neither publint nor `attw` catches a wildcard whose targets are missing.
50
52
 
51
- | Module | Exports |
52
- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
- | `zip/container` | `zipPackage` (ordered-entries ZIP write with stored-uncompressed support), `unzipPackage`, `ZipEntry` |
54
- | `zip/detect` | `detectArchiveFormat` (`'zip' \| 'cfb' \| 'unknown'`), `isZipArchive`, `ArchiveFormat` |
55
- | `zip/walk` | `walkArchive` (recursive ZIP-in-ZIP walking), `ArchiveWalkEntry`, `ArchiveWalkLimitError`, `MAX_WALK_DEPTH`, `MAX_WALK_TOTAL_BYTES`, `WalkArchiveOptions` |
56
- | `cfb/detect` | `isCompoundFile` (the `D0 CF 11 E0 …` magic-byte check) |
57
- | `cfb/read` | `readCompoundFile` (bounded [MS-CFB] stream extraction), `CompoundFileStream`, `CompoundFileFormatError`, `MAX_CFB_TOTAL_STREAM_BYTES`, `ReadCompoundFileOptions` |
58
- | `cfb/write` | `writeCompoundFile` ([MS-CFB] container generation), `CompoundFileWriteError`, `WriteCompoundFileOptions` — takes the `CompoundFileStream` array `cfb/read` returns |
59
- | `cfb/ole-package` | `readOlePackage` (OLE Package stream unwrapping), `writeOlePackage` (the mirror), `OlePackage`, `OlePackageFormatError`, `OlePackageWriteError` |
60
- | `oleps/read` | `readPropertySetStream` (generic [MS-OLEPS] property-set decoding), `PropertySetFormatError` |
61
- | `oleps/write` | `writePropertySetStream` (generic [MS-OLEPS] property-set encoding), `PropertySetWriteError` — takes the `PropertySet` shape `oleps/read` returns |
62
- | `oleps/summary-information` | `readSummaryInformation`, `writeSummaryInformationStream`, `SummaryInformationProperties`, `FMTID_SUMMARY_INFORMATION` |
63
- | `oleps/layout-metadata` | `summaryInformationToLayoutMetadata`, `layoutMetadataToSummaryInformation`, `hasSummaryInformationFields` — the `SummaryInformationProperties` <-> `document-schema.js`'s `LayoutMetadata` mapping, shared by `doc-codec`/`xls-codec`/`ppt-codec` (see [Property sets](#property-sets)) |
64
- | `crypto/md5` | `md5` — RFC 1321 MD5, hand-written (see [Legacy Office encryption](#legacy-office-encryption)) |
65
- | `crypto/rc4` | `rc4` — the RC4 stream cipher, hand-written; symmetric, so also the decrypt operation |
66
- | `crypto/office-rc4` | `deriveOfficeRc4BaseHash`, `deriveOfficeRc4BlockKey`, `decryptOfficeRc4`, `OFFICE_RC4_VERIFIER_LENGTH`, `OFFICE_RC4_BLOCK_SIZE`, `OFFICE_RC4_DOC_BLOCK_SIZE` [MS-OFFCRYPTO] 2.3.6.2's own key derivation and the block-keyed stream decryption built on it |
53
+ | Module | Exports |
54
+ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `zip/container` | `zipPackage` (ordered-entries ZIP write with stored-uncompressed support), `unzipPackage`, `ZipEntry` |
56
+ | `zip/detect` | `detectArchiveFormat` (`'zip' \| 'cfb' \| 'unknown'`), `isZipArchive`, `ArchiveFormat` |
57
+ | `zip/walk` | `walkArchive` (recursive ZIP-in-ZIP walking), `ArchiveWalkEntry`, `ArchiveWalkLimitError`, `MAX_WALK_DEPTH`, `MAX_WALK_TOTAL_BYTES`, `WalkArchiveOptions` |
58
+ | `cfb/detect` | `isCompoundFile` (the `D0 CF 11 E0 …` magic-byte check) |
59
+ | `cfb/read` | `readCompoundFile` (bounded [MS-CFB] stream extraction), `CompoundFileStream`, `CompoundFileFormatError`, `MAX_CFB_TOTAL_STREAM_BYTES`, `ReadCompoundFileOptions` |
60
+ | `cfb/write` | `writeCompoundFile` ([MS-CFB] container generation), `CompoundFileWriteError`, `WriteCompoundFileOptions` — takes the `CompoundFileStream` array `cfb/read` returns |
61
+ | `cfb/ole-package` | `readOlePackage` (OLE Package stream unwrapping), `writeOlePackage` (the mirror), `OlePackage`, `OlePackageFormatError`, `OlePackageWriteError` |
62
+ | `oleps/read` | `readPropertySetStream` (generic [MS-OLEPS] property-set decoding), `PropertySetFormatError` |
63
+ | `oleps/write` | `writePropertySetStream` (generic [MS-OLEPS] property-set encoding), `PropertySetWriteError` — takes the `PropertySet` shape `oleps/read` returns |
64
+ | `oleps/summary-information` | `readSummaryInformation`, `writeSummaryInformationStream`, `SummaryInformationProperties`, `FMTID_SUMMARY_INFORMATION` |
65
+ | `oleps/layout-metadata` | `summaryInformationToLayoutMetadata`, `layoutMetadataToSummaryInformation`, `hasSummaryInformationFields` — the `SummaryInformationProperties` <-> `document-schema.js`'s `LayoutMetadata` mapping, shared by `doc-codec`/`xls-codec`/`ppt-codec` (see [Property sets](#property-sets)) |
66
+ | `crypto/md5` | `md5` — RFC 1321 MD5, hand-written (see [Legacy Office encryption](#legacy-office-encryption)) |
67
+ | `crypto/sha1` | `sha1` — RFC 3174/FIPS 180-1 SHA-1, hand-written (see [RC4 CryptoAPI encryption](#rc4-cryptoapi-encryption-ppt)) |
68
+ | `crypto/rc4` | `rc4` the RC4 stream cipher, hand-written; symmetric, so also the decrypt operation |
69
+ | `crypto/office-rc4` | `deriveOfficeRc4BaseHash`, `deriveOfficeRc4BlockKey`, `decryptOfficeRc4`, `OFFICE_RC4_VERIFIER_LENGTH`, `OFFICE_RC4_BLOCK_SIZE`, `OFFICE_RC4_DOC_BLOCK_SIZE` — [MS-OFFCRYPTO] 2.3.6.2's own key derivation and the block-keyed stream decryption built on it |
70
+ | `crypto/office-rc4-cryptoapi` | `deriveRc4CryptoApiBlockKey`, `verifyRc4CryptoApiPassword`, `RC4_CRYPTOAPI_SALT_LENGTH`, `RC4_CRYPTOAPI_VERIFIER_LENGTH`, `RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH`, `RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS` — [MS-OFFCRYPTO] 2.3.5.2's own key derivation, `.ppt`'s own encryption scheme |
67
71
 
68
72
  ### Recursive walking
69
73
 
@@ -188,7 +192,37 @@ const plaintext = decryptOfficeRc4(baseHash, streamOffset, ciphertext);
188
192
 
189
193
  `deriveOfficeRc4BaseHash`/`deriveOfficeRc4BlockKey`/`decryptOfficeRc4` implement [MS-OFFCRYPTO] 2.3.6.1/2.3.6.2's RC4 encryption header scheme: a per-workbook base hash derived once from the password and the header's own salt, then a full 128-bit RC4 key re-derived from that base hash at every 1024-byte boundary of the underlying decrypted stream — despite [MS-OFFCRYPTO] 2.3.6.1's own field descriptions twice stating "encrypted using a 40-bit RC4 cipher", which is wrong; the real key is Hfinal in full, confirmed against Apache POI's `BinaryRC4Decryptor` and nolze/msoffcrypto-tool's own doctested test vectors, both cross-checked directly rather than trusted from the spec's own prose — see each constant's own doc comment for the full account, including the truncated-to-5-bytes implementation this package shipped briefly before the cross-check caught it. `decryptOfficeRc4` takes the byte offset a chunk starts at within the whole encrypted stream (not within the chunk itself), so a stream can be decrypted in arbitrary pieces, not only from its own start, and still land on the correct per-block key throughout. The 1024-byte re-keying interval is `decryptOfficeRc4`'s own default `blockSize`, since [MS-XLS]'s own FilePass scheme is this module's original consumer, but it is a real [MS-XLS]-specific value, not a property of the algorithm itself: [MS-DOC] 2.2.6.2's own RC4 encryption header (the identical [MS-OFFCRYPTO] 2.3.6.1 structure, confirmed against Apache POI's `EncryptionMode.binaryRC4` resolving both) re-keys every 512 bytes instead (`OFFICE_RC4_DOC_BLOCK_SIZE`), passed explicitly as `decryptOfficeRc4`'s fourth argument.
190
194
 
191
- This is exactly the piece `xls-codec`'s `FilePass`-record reader and `doc-codec`'s own `EncryptionHeader` reader (ExaDev/documents.js#1113) both need and nothing more: locating the header, reading its own fields, and verifying the password against `EncryptedVerifier`/`EncryptedVerifierHash` are each codec's own concern, not this package's — `archive-codec` carries only the format-agnostic cryptography, never a `.xls`- or `.doc`-specific byte layout. `decryptOfficeRc4`'s own `blockSize` parameter exists because the two codecs' re-keying intervals genuinely differ (`OFFICE_RC4_BLOCK_SIZE`, 1024, for `xls-codec`; `OFFICE_RC4_DOC_BLOCK_SIZE`, 512, for `doc-codec`) -- confirmed as real, independent values against Apache POI's own `Biff8DecryptingStream` and `BinaryRC4Decryptor` respectively, not one shared constant this package could have assumed. `md5`/`rc4` are exported individually too, for `ppt-codec`'s own eventual integration against the identical header scheme.
195
+ This is exactly the piece `xls-codec`'s `FilePass`-record reader and `doc-codec`'s own `EncryptionHeader` reader (ExaDev/documents.js#1113) both need and nothing more: locating the header, reading its own fields, and verifying the password against `EncryptedVerifier`/`EncryptedVerifierHash` are each codec's own concern, not this package's — `archive-codec` carries only the format-agnostic cryptography, never a `.xls`- or `.doc`-specific byte layout. `decryptOfficeRc4`'s own `blockSize` parameter exists because the two codecs' re-keying intervals genuinely differ (`OFFICE_RC4_BLOCK_SIZE`, 1024, for `xls-codec`; `OFFICE_RC4_DOC_BLOCK_SIZE`, 512, for `doc-codec`) -- confirmed as real, independent values against Apache POI's own `Biff8DecryptingStream` and `BinaryRC4Decryptor` respectively, not one shared constant this package could have assumed. `md5`/`rc4` are exported individually too, for reuse by the RC4 CryptoAPI scheme below, which is symmetric enough to need `rc4` but not this module's own MD5-based key derivation.
196
+
197
+ ### RC4 CryptoAPI encryption (.ppt)
198
+
199
+ ```ts
200
+ import {
201
+ deriveRc4CryptoApiBlockKey,
202
+ verifyRc4CryptoApiPassword,
203
+ rc4,
204
+ } from "archive-codec";
205
+
206
+ // salt, keySizeBits, and the encrypted verifier fields come from the caller's
207
+ // own DocumentEncryptionAtom reading (ppt-codec's own [MS-PPT] 2.3.7 handling,
208
+ // not this package's concern).
209
+ if (
210
+ !verifyRc4CryptoApiPassword(
211
+ password,
212
+ salt,
213
+ keySizeBits,
214
+ encryptedVerifier,
215
+ encryptedVerifierHash,
216
+ )
217
+ ) {
218
+ throw new Error("wrong password");
219
+ }
220
+ // block is the persist object's own persist ID, not a byte offset -- see below.
221
+ const key = deriveRc4CryptoApiBlockKey(password, salt, block, keySizeBits);
222
+ const plaintext = rc4(key, ciphertext);
223
+ ```
224
+
225
+ `deriveRc4CryptoApiBlockKey`/`verifyRc4CryptoApiPassword` implement [MS-OFFCRYPTO] 2.3.5.1/2.3.5.2's "RC4 CryptoAPI Encryption" scheme, `.ppt`'s own encryption ([documents.js#1116](https://github.com/ExaDev/documents.js/issues/1116)) -- genuinely different from the [Legacy Office encryption](#legacy-office-encryption) scheme above despite both ending in an RC4 keystream: SHA-1 rather than MD5, one hash of the salt and password folded with the block number rather than an intermediate 336-byte buffer, and (per the spec's own prose) explicitly not iterated. There is no `decryptOfficeRc4`-style entry point with its own block-boundary loop here, because RC4 CryptoAPI does not re-key at fixed byte intervals within one continuous stream the way the legacy scheme does: it re-keys per persist object, using that object's own persist ID as the "block number" -- a caller derives the one key its own object needs and applies this package's own `rc4` directly, exactly as `ppt-codec`'s `decryptPptDocumentStream` does. Cross-checked against Apache POI's `EncryptionInfo`/`StandardEncryptionHeader`/`CryptoAPIEncryptionHeader` and nolze/msoffcrypto-tool's `method/rc4_cryptoapi.py`, whose own `_makekey`/`verifypw` this module's derivation and verification mirror.
192
226
 
193
227
  ### ZIP container
194
228
 
@@ -0,0 +1,62 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_crypto_sha1 = require("./sha1.cjs");
3
+ const require_crypto_rc4 = require("./rc4.cjs");
4
+ const require_crypto_office_rc4 = require("./office-rc4.cjs");
5
+ //#region src/crypto/office-rc4-cryptoapi.ts
6
+ /** [MS-OFFCRYPTO] 2.3.5.1: "If set to 0x00000000, [keySize] MUST be interpreted as 0x00000028 bits" -- confirmed against Apache POI's `StandardEncryptionHeader` constructor, which special-cases exactly this value before calling `setKeySize`. */
7
+ const RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = 40;
8
+ /** The salt length [MS-OFFCRYPTO] 2.3.4.6's EncryptionVerifier (shared by the CryptoAPI and Standard/AES schemes alike) fixes for every cipher: "the SaltSize field... MUST be 0x00000010" -- confirmed against Apache POI's `StandardEncryptionVerifier`, which throws if the on-disk saltSize field disagrees. */
9
+ const RC4_CRYPTOAPI_SALT_LENGTH = 16;
10
+ /** The EncryptedVerifier field's own fixed length, [MS-OFFCRYPTO] 2.3.4.6/2.3.4.9: "an array of 16 bytes." */
11
+ const RC4_CRYPTOAPI_VERIFIER_LENGTH = 16;
12
+ /** EncryptedVerifierHash's length for RC4 specifically (as opposed to AES's 32): [MS-OFFCRYPTO] 2.3.4.9 -- "If the encryption algorithm is RC4, the length MUST be 20 bytes" -- confirmed against Apache POI's `CipherAlgorithm.rc4.encryptedVerifierHashLength`. This module implements RC4 only, so this constant is unconditional here (a caller finding algId names AES instead has found a file outside this module's scope, per the top comment). */
13
+ const RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = 20;
14
+ function keySizeBitsOf(keySizeBits) {
15
+ return keySizeBits === 0 ? 40 : keySizeBits;
16
+ }
17
+ function bytesEqual(a, b) {
18
+ if (a.length !== b.length) return false;
19
+ return a.every((byte, index) => byte === b[index]);
20
+ }
21
+ /** The per-persist-object RC4 key: SHA1(SHA1(salt + UTF-16LE password) + the block number as 4 little-endian bytes), truncated to `keySizeBits`. [MS-OFFCRYPTO] 2.3.5.1's own 40-bit special case is real, not a rounding artefact: a 40-bit key is still carried in a 16-byte buffer (the derived hash's own first 5 bytes, zero-padded to 16), confirmed directly against nolze/msoffcrypto-tool's `_makekey` (`key = hfinal[:5] + b"\x00" * 11`) -- the "effective" key strength is 40 bits, but the RC4 key schedule this package's own `rc4` runs still consumes all 16 bytes of it. */
22
+ function deriveRc4CryptoApiBlockKey(password, salt, block, keySizeBits) {
23
+ const passwordBytes = require_crypto_office_rc4.passwordToUtf16LeBytes(password);
24
+ const saltAndPassword = new Uint8Array(salt.length + passwordBytes.length);
25
+ saltAndPassword.set(salt, 0);
26
+ saltAndPassword.set(passwordBytes, salt.length);
27
+ const h0 = require_crypto_sha1.sha1(saltAndPassword);
28
+ const blockBytes = /* @__PURE__ */ new Uint8Array(4);
29
+ blockBytes[0] = block & 255;
30
+ blockBytes[1] = block >>> 8 & 255;
31
+ blockBytes[2] = block >>> 16 & 255;
32
+ blockBytes[3] = block >>> 24 & 255;
33
+ const h0AndBlock = new Uint8Array(h0.length + blockBytes.length);
34
+ h0AndBlock.set(h0, 0);
35
+ h0AndBlock.set(blockBytes, h0.length);
36
+ const hfinal = require_crypto_sha1.sha1(h0AndBlock);
37
+ const effectiveKeySizeBits = keySizeBitsOf(keySizeBits);
38
+ if (effectiveKeySizeBits === 40) {
39
+ const key = /* @__PURE__ */ new Uint8Array(16);
40
+ key.set(hfinal.subarray(0, 5), 0);
41
+ return key;
42
+ }
43
+ return hfinal.subarray(0, effectiveKeySizeBits / 8);
44
+ }
45
+ /** Verifies a candidate password against a DocumentEncryptionAtom's own EncryptionVerifier fields ([MS-OFFCRYPTO] 2.3.4.9): the block-0 key decrypts `encryptedVerifier` immediately followed by `encryptedVerifierHash` as ONE continuous 36-byte RC4 keystream application -- not two independent decryptions, each of which would wrongly restart the keystream at its own start -- and the password is correct exactly when SHA1 of the decrypted 16-byte verifier equals the decrypted hash's own first 20 bytes. */
46
+ function verifyRc4CryptoApiPassword(password, salt, keySizeBits, encryptedVerifier, encryptedVerifierHash) {
47
+ const key = deriveRc4CryptoApiBlockKey(password, salt, 0, keySizeBits);
48
+ const combined = new Uint8Array(encryptedVerifier.length + encryptedVerifierHash.length);
49
+ combined.set(encryptedVerifier, 0);
50
+ combined.set(encryptedVerifierHash, encryptedVerifier.length);
51
+ const decrypted = require_crypto_rc4.rc4(key, combined);
52
+ const verifier = decrypted.subarray(0, encryptedVerifier.length);
53
+ const hash = decrypted.subarray(encryptedVerifier.length, encryptedVerifier.length + 20);
54
+ return bytesEqual(require_crypto_sha1.sha1(verifier), hash);
55
+ }
56
+ //#endregion
57
+ exports.RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS;
58
+ exports.RC4_CRYPTOAPI_SALT_LENGTH = RC4_CRYPTOAPI_SALT_LENGTH;
59
+ exports.RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH;
60
+ exports.RC4_CRYPTOAPI_VERIFIER_LENGTH = RC4_CRYPTOAPI_VERIFIER_LENGTH;
61
+ exports.deriveRc4CryptoApiBlockKey = deriveRc4CryptoApiBlockKey;
62
+ exports.verifyRc4CryptoApiPassword = verifyRc4CryptoApiPassword;
@@ -0,0 +1,15 @@
1
+ //#region src/crypto/office-rc4-cryptoapi.d.ts
2
+ /** [MS-OFFCRYPTO] 2.3.5.1: "If set to 0x00000000, [keySize] MUST be interpreted as 0x00000028 bits" -- confirmed against Apache POI's `StandardEncryptionHeader` constructor, which special-cases exactly this value before calling `setKeySize`. */
3
+ declare const RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = 40;
4
+ /** The salt length [MS-OFFCRYPTO] 2.3.4.6's EncryptionVerifier (shared by the CryptoAPI and Standard/AES schemes alike) fixes for every cipher: "the SaltSize field... MUST be 0x00000010" -- confirmed against Apache POI's `StandardEncryptionVerifier`, which throws if the on-disk saltSize field disagrees. */
5
+ declare const RC4_CRYPTOAPI_SALT_LENGTH = 16;
6
+ /** The EncryptedVerifier field's own fixed length, [MS-OFFCRYPTO] 2.3.4.6/2.3.4.9: "an array of 16 bytes." */
7
+ declare const RC4_CRYPTOAPI_VERIFIER_LENGTH = 16;
8
+ /** EncryptedVerifierHash's length for RC4 specifically (as opposed to AES's 32): [MS-OFFCRYPTO] 2.3.4.9 -- "If the encryption algorithm is RC4, the length MUST be 20 bytes" -- confirmed against Apache POI's `CipherAlgorithm.rc4.encryptedVerifierHashLength`. This module implements RC4 only, so this constant is unconditional here (a caller finding algId names AES instead has found a file outside this module's scope, per the top comment). */
9
+ declare const RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = 20;
10
+ /** The per-persist-object RC4 key: SHA1(SHA1(salt + UTF-16LE password) + the block number as 4 little-endian bytes), truncated to `keySizeBits`. [MS-OFFCRYPTO] 2.3.5.1's own 40-bit special case is real, not a rounding artefact: a 40-bit key is still carried in a 16-byte buffer (the derived hash's own first 5 bytes, zero-padded to 16), confirmed directly against nolze/msoffcrypto-tool's `_makekey` (`key = hfinal[:5] + b"\x00" * 11`) -- the "effective" key strength is 40 bits, but the RC4 key schedule this package's own `rc4` runs still consumes all 16 bytes of it. */
11
+ declare function deriveRc4CryptoApiBlockKey(password: string, salt: Uint8Array<ArrayBuffer>, block: number, keySizeBits: number): Uint8Array<ArrayBuffer>;
12
+ /** Verifies a candidate password against a DocumentEncryptionAtom's own EncryptionVerifier fields ([MS-OFFCRYPTO] 2.3.4.9): the block-0 key decrypts `encryptedVerifier` immediately followed by `encryptedVerifierHash` as ONE continuous 36-byte RC4 keystream application -- not two independent decryptions, each of which would wrongly restart the keystream at its own start -- and the password is correct exactly when SHA1 of the decrypted 16-byte verifier equals the decrypted hash's own first 20 bytes. */
13
+ declare function verifyRc4CryptoApiPassword(password: string, salt: Uint8Array<ArrayBuffer>, keySizeBits: number, encryptedVerifier: Uint8Array<ArrayBuffer>, encryptedVerifierHash: Uint8Array<ArrayBuffer>): boolean;
14
+ //#endregion
15
+ export { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword };
@@ -0,0 +1,15 @@
1
+ //#region src/crypto/office-rc4-cryptoapi.d.ts
2
+ /** [MS-OFFCRYPTO] 2.3.5.1: "If set to 0x00000000, [keySize] MUST be interpreted as 0x00000028 bits" -- confirmed against Apache POI's `StandardEncryptionHeader` constructor, which special-cases exactly this value before calling `setKeySize`. */
3
+ declare const RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = 40;
4
+ /** The salt length [MS-OFFCRYPTO] 2.3.4.6's EncryptionVerifier (shared by the CryptoAPI and Standard/AES schemes alike) fixes for every cipher: "the SaltSize field... MUST be 0x00000010" -- confirmed against Apache POI's `StandardEncryptionVerifier`, which throws if the on-disk saltSize field disagrees. */
5
+ declare const RC4_CRYPTOAPI_SALT_LENGTH = 16;
6
+ /** The EncryptedVerifier field's own fixed length, [MS-OFFCRYPTO] 2.3.4.6/2.3.4.9: "an array of 16 bytes." */
7
+ declare const RC4_CRYPTOAPI_VERIFIER_LENGTH = 16;
8
+ /** EncryptedVerifierHash's length for RC4 specifically (as opposed to AES's 32): [MS-OFFCRYPTO] 2.3.4.9 -- "If the encryption algorithm is RC4, the length MUST be 20 bytes" -- confirmed against Apache POI's `CipherAlgorithm.rc4.encryptedVerifierHashLength`. This module implements RC4 only, so this constant is unconditional here (a caller finding algId names AES instead has found a file outside this module's scope, per the top comment). */
9
+ declare const RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = 20;
10
+ /** The per-persist-object RC4 key: SHA1(SHA1(salt + UTF-16LE password) + the block number as 4 little-endian bytes), truncated to `keySizeBits`. [MS-OFFCRYPTO] 2.3.5.1's own 40-bit special case is real, not a rounding artefact: a 40-bit key is still carried in a 16-byte buffer (the derived hash's own first 5 bytes, zero-padded to 16), confirmed directly against nolze/msoffcrypto-tool's `_makekey` (`key = hfinal[:5] + b"\x00" * 11`) -- the "effective" key strength is 40 bits, but the RC4 key schedule this package's own `rc4` runs still consumes all 16 bytes of it. */
11
+ declare function deriveRc4CryptoApiBlockKey(password: string, salt: Uint8Array<ArrayBuffer>, block: number, keySizeBits: number): Uint8Array<ArrayBuffer>;
12
+ /** Verifies a candidate password against a DocumentEncryptionAtom's own EncryptionVerifier fields ([MS-OFFCRYPTO] 2.3.4.9): the block-0 key decrypts `encryptedVerifier` immediately followed by `encryptedVerifierHash` as ONE continuous 36-byte RC4 keystream application -- not two independent decryptions, each of which would wrongly restart the keystream at its own start -- and the password is correct exactly when SHA1 of the decrypted 16-byte verifier equals the decrypted hash's own first 20 bytes. */
13
+ declare function verifyRc4CryptoApiPassword(password: string, salt: Uint8Array<ArrayBuffer>, keySizeBits: number, encryptedVerifier: Uint8Array<ArrayBuffer>, encryptedVerifierHash: Uint8Array<ArrayBuffer>): boolean;
14
+ //#endregion
15
+ export { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword };
@@ -0,0 +1,56 @@
1
+ import { sha1 } from "./sha1.js";
2
+ import { rc4 } from "./rc4.js";
3
+ import { passwordToUtf16LeBytes } from "./office-rc4.js";
4
+ //#region src/crypto/office-rc4-cryptoapi.ts
5
+ /** [MS-OFFCRYPTO] 2.3.5.1: "If set to 0x00000000, [keySize] MUST be interpreted as 0x00000028 bits" -- confirmed against Apache POI's `StandardEncryptionHeader` constructor, which special-cases exactly this value before calling `setKeySize`. */
6
+ const RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = 40;
7
+ /** The salt length [MS-OFFCRYPTO] 2.3.4.6's EncryptionVerifier (shared by the CryptoAPI and Standard/AES schemes alike) fixes for every cipher: "the SaltSize field... MUST be 0x00000010" -- confirmed against Apache POI's `StandardEncryptionVerifier`, which throws if the on-disk saltSize field disagrees. */
8
+ const RC4_CRYPTOAPI_SALT_LENGTH = 16;
9
+ /** The EncryptedVerifier field's own fixed length, [MS-OFFCRYPTO] 2.3.4.6/2.3.4.9: "an array of 16 bytes." */
10
+ const RC4_CRYPTOAPI_VERIFIER_LENGTH = 16;
11
+ /** EncryptedVerifierHash's length for RC4 specifically (as opposed to AES's 32): [MS-OFFCRYPTO] 2.3.4.9 -- "If the encryption algorithm is RC4, the length MUST be 20 bytes" -- confirmed against Apache POI's `CipherAlgorithm.rc4.encryptedVerifierHashLength`. This module implements RC4 only, so this constant is unconditional here (a caller finding algId names AES instead has found a file outside this module's scope, per the top comment). */
12
+ const RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = 20;
13
+ function keySizeBitsOf(keySizeBits) {
14
+ return keySizeBits === 0 ? 40 : keySizeBits;
15
+ }
16
+ function bytesEqual(a, b) {
17
+ if (a.length !== b.length) return false;
18
+ return a.every((byte, index) => byte === b[index]);
19
+ }
20
+ /** The per-persist-object RC4 key: SHA1(SHA1(salt + UTF-16LE password) + the block number as 4 little-endian bytes), truncated to `keySizeBits`. [MS-OFFCRYPTO] 2.3.5.1's own 40-bit special case is real, not a rounding artefact: a 40-bit key is still carried in a 16-byte buffer (the derived hash's own first 5 bytes, zero-padded to 16), confirmed directly against nolze/msoffcrypto-tool's `_makekey` (`key = hfinal[:5] + b"\x00" * 11`) -- the "effective" key strength is 40 bits, but the RC4 key schedule this package's own `rc4` runs still consumes all 16 bytes of it. */
21
+ function deriveRc4CryptoApiBlockKey(password, salt, block, keySizeBits) {
22
+ const passwordBytes = passwordToUtf16LeBytes(password);
23
+ const saltAndPassword = new Uint8Array(salt.length + passwordBytes.length);
24
+ saltAndPassword.set(salt, 0);
25
+ saltAndPassword.set(passwordBytes, salt.length);
26
+ const h0 = sha1(saltAndPassword);
27
+ const blockBytes = /* @__PURE__ */ new Uint8Array(4);
28
+ blockBytes[0] = block & 255;
29
+ blockBytes[1] = block >>> 8 & 255;
30
+ blockBytes[2] = block >>> 16 & 255;
31
+ blockBytes[3] = block >>> 24 & 255;
32
+ const h0AndBlock = new Uint8Array(h0.length + blockBytes.length);
33
+ h0AndBlock.set(h0, 0);
34
+ h0AndBlock.set(blockBytes, h0.length);
35
+ const hfinal = sha1(h0AndBlock);
36
+ const effectiveKeySizeBits = keySizeBitsOf(keySizeBits);
37
+ if (effectiveKeySizeBits === 40) {
38
+ const key = /* @__PURE__ */ new Uint8Array(16);
39
+ key.set(hfinal.subarray(0, 5), 0);
40
+ return key;
41
+ }
42
+ return hfinal.subarray(0, effectiveKeySizeBits / 8);
43
+ }
44
+ /** Verifies a candidate password against a DocumentEncryptionAtom's own EncryptionVerifier fields ([MS-OFFCRYPTO] 2.3.4.9): the block-0 key decrypts `encryptedVerifier` immediately followed by `encryptedVerifierHash` as ONE continuous 36-byte RC4 keystream application -- not two independent decryptions, each of which would wrongly restart the keystream at its own start -- and the password is correct exactly when SHA1 of the decrypted 16-byte verifier equals the decrypted hash's own first 20 bytes. */
45
+ function verifyRc4CryptoApiPassword(password, salt, keySizeBits, encryptedVerifier, encryptedVerifierHash) {
46
+ const key = deriveRc4CryptoApiBlockKey(password, salt, 0, keySizeBits);
47
+ const combined = new Uint8Array(encryptedVerifier.length + encryptedVerifierHash.length);
48
+ combined.set(encryptedVerifier, 0);
49
+ combined.set(encryptedVerifierHash, encryptedVerifier.length);
50
+ const decrypted = rc4(key, combined);
51
+ const verifier = decrypted.subarray(0, encryptedVerifier.length);
52
+ const hash = decrypted.subarray(encryptedVerifier.length, encryptedVerifier.length + 20);
53
+ return bytesEqual(sha1(verifier), hash);
54
+ }
55
+ //#endregion
56
+ export { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword };
@@ -10,6 +10,7 @@ const OFFICE_RC4_BLOCK_SIZE = 1024;
10
10
  const OFFICE_RC4_DOC_BLOCK_SIZE = 512;
11
11
  /** The intermediate H0/H1 truncation [MS-OFFCRYPTO] 2.3.6.2 states explicitly ("H0's own first 5 bytes", "H1's own first 5 bytes") -- distinct from the FINAL per-block key length, which is Hfinal in full (see this file's own top comment) and is never truncated. */
12
12
  const INTERMEDIATE_HASH_LENGTH_BYTES = 5;
13
+ /** UTF-16LE password encoding, shared with office-rc4-cryptoapi.ts: both [MS-OFFCRYPTO] key-derivation schemes this package implements hash a password in this same encoding, per their own respective specs (2.3.6.2 and 2.3.5.2). */
13
14
  function passwordToUtf16LeBytes(password) {
14
15
  const bytes = new Uint8Array(password.length * 2);
15
16
  for (let i = 0; i < password.length; i += 1) {
@@ -70,3 +71,4 @@ exports.OFFICE_RC4_VERIFIER_LENGTH = OFFICE_RC4_VERIFIER_LENGTH;
70
71
  exports.decryptOfficeRc4 = decryptOfficeRc4;
71
72
  exports.deriveOfficeRc4BaseHash = deriveOfficeRc4BaseHash;
72
73
  exports.deriveOfficeRc4BlockKey = deriveOfficeRc4BlockKey;
74
+ exports.passwordToUtf16LeBytes = passwordToUtf16LeBytes;
@@ -5,6 +5,8 @@ declare const OFFICE_RC4_VERIFIER_LENGTH = 16;
5
5
  declare const OFFICE_RC4_BLOCK_SIZE = 1024;
6
6
  /** [MS-DOC] 2.2.6.2's own re-keying interval for its identical RC4 encryption header scheme -- stated directly in the spec's own prose ("encrypted in 512-byte blocks"), confirmed independently against Apache POI's BinaryRC4Decryptor (`chunkSize = 512`). Genuinely different from OFFICE_RC4_BLOCK_SIZE, not a duplicate of it -- pass this as decryptOfficeRc4's own `blockSize` argument when decrypting a .doc stream. */
7
7
  declare const OFFICE_RC4_DOC_BLOCK_SIZE = 512;
8
+ /** UTF-16LE password encoding, shared with office-rc4-cryptoapi.ts: both [MS-OFFCRYPTO] key-derivation schemes this package implements hash a password in this same encoding, per their own respective specs (2.3.6.2 and 2.3.5.2). */
9
+ declare function passwordToUtf16LeBytes(password: string): Uint8Array<ArrayBuffer>;
8
10
  /** The per-workbook intermediate hash (H1's own first 5 bytes) every block's key derives from -- computed once per password+salt pair, then reused across every block via deriveOfficeRc4BlockKey, since recomputing H0/H1 per block would be needless repeated work over the identical 336-byte buffer. */
9
11
  declare function deriveOfficeRc4BaseHash(password: string, salt: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
10
12
  /** The real, block-specific RC4 key: MD5(baseHash + the block number as 4 little-endian bytes) in full -- all 16 bytes, never truncated (see this file's own top comment for why that matters). */
@@ -16,4 +18,4 @@ declare function deriveOfficeRc4BlockKey(baseHash: Uint8Array<ArrayBuffer>, bloc
16
18
  */
17
19
  declare function decryptOfficeRc4(baseHash: Uint8Array<ArrayBuffer>, streamOffset: number, data: Uint8Array<ArrayBuffer>, blockSize?: number): Uint8Array<ArrayBuffer>;
18
20
  //#endregion
19
- export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey };
21
+ export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes };
@@ -5,6 +5,8 @@ declare const OFFICE_RC4_VERIFIER_LENGTH = 16;
5
5
  declare const OFFICE_RC4_BLOCK_SIZE = 1024;
6
6
  /** [MS-DOC] 2.2.6.2's own re-keying interval for its identical RC4 encryption header scheme -- stated directly in the spec's own prose ("encrypted in 512-byte blocks"), confirmed independently against Apache POI's BinaryRC4Decryptor (`chunkSize = 512`). Genuinely different from OFFICE_RC4_BLOCK_SIZE, not a duplicate of it -- pass this as decryptOfficeRc4's own `blockSize` argument when decrypting a .doc stream. */
7
7
  declare const OFFICE_RC4_DOC_BLOCK_SIZE = 512;
8
+ /** UTF-16LE password encoding, shared with office-rc4-cryptoapi.ts: both [MS-OFFCRYPTO] key-derivation schemes this package implements hash a password in this same encoding, per their own respective specs (2.3.6.2 and 2.3.5.2). */
9
+ declare function passwordToUtf16LeBytes(password: string): Uint8Array<ArrayBuffer>;
8
10
  /** The per-workbook intermediate hash (H1's own first 5 bytes) every block's key derives from -- computed once per password+salt pair, then reused across every block via deriveOfficeRc4BlockKey, since recomputing H0/H1 per block would be needless repeated work over the identical 336-byte buffer. */
9
11
  declare function deriveOfficeRc4BaseHash(password: string, salt: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
10
12
  /** The real, block-specific RC4 key: MD5(baseHash + the block number as 4 little-endian bytes) in full -- all 16 bytes, never truncated (see this file's own top comment for why that matters). */
@@ -16,4 +18,4 @@ declare function deriveOfficeRc4BlockKey(baseHash: Uint8Array<ArrayBuffer>, bloc
16
18
  */
17
19
  declare function decryptOfficeRc4(baseHash: Uint8Array<ArrayBuffer>, streamOffset: number, data: Uint8Array<ArrayBuffer>, blockSize?: number): Uint8Array<ArrayBuffer>;
18
20
  //#endregion
19
- export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey };
21
+ export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes };
@@ -9,6 +9,7 @@ const OFFICE_RC4_BLOCK_SIZE = 1024;
9
9
  const OFFICE_RC4_DOC_BLOCK_SIZE = 512;
10
10
  /** The intermediate H0/H1 truncation [MS-OFFCRYPTO] 2.3.6.2 states explicitly ("H0's own first 5 bytes", "H1's own first 5 bytes") -- distinct from the FINAL per-block key length, which is Hfinal in full (see this file's own top comment) and is never truncated. */
11
11
  const INTERMEDIATE_HASH_LENGTH_BYTES = 5;
12
+ /** UTF-16LE password encoding, shared with office-rc4-cryptoapi.ts: both [MS-OFFCRYPTO] key-derivation schemes this package implements hash a password in this same encoding, per their own respective specs (2.3.6.2 and 2.3.5.2). */
12
13
  function passwordToUtf16LeBytes(password) {
13
14
  const bytes = new Uint8Array(password.length * 2);
14
15
  for (let i = 0; i < password.length; i += 1) {
@@ -63,4 +64,4 @@ function decryptOfficeRc4(baseHash, streamOffset, data, blockSize = OFFICE_RC4_B
63
64
  return out;
64
65
  }
65
66
  //#endregion
66
- export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey };
67
+ export { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes };
@@ -0,0 +1,89 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/crypto/sha1.ts
3
+ const BLOCK_BYTES = 64;
4
+ const DIGEST_BYTES = 20;
5
+ const ROUNDS = 80;
6
+ const WORDS_PER_BLOCK = 16;
7
+ const H0_INIT = 1732584193;
8
+ const H1_INIT = 4023233417;
9
+ const H2_INIT = 2562383102;
10
+ const H3_INIT = 271733878;
11
+ const H4_INIT = 3285377520;
12
+ function rotateLeft(value, bits) {
13
+ return (value << bits | value >>> 32 - bits) >>> 0;
14
+ }
15
+ function pad(message) {
16
+ const bitLength = message.length * 8;
17
+ const paddedLength = Math.ceil((message.length + 9) / BLOCK_BYTES) * BLOCK_BYTES;
18
+ const buffer = new ArrayBuffer(paddedLength);
19
+ const view = new DataView(buffer);
20
+ new Uint8Array(buffer).set(message, 0);
21
+ view.setUint8(message.length, 128);
22
+ view.setUint32(paddedLength - 8, 0);
23
+ view.setUint32(paddedLength - 4, bitLength >>> 0);
24
+ return view;
25
+ }
26
+ function sha1(message) {
27
+ const padded = pad(message);
28
+ const blockCount = padded.byteLength / BLOCK_BYTES;
29
+ let h0 = H0_INIT;
30
+ let h1 = H1_INIT;
31
+ let h2 = H2_INIT;
32
+ let h3 = H3_INIT;
33
+ let h4 = H4_INIT;
34
+ const schedule = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(320));
35
+ for (let block = 0; block < blockCount; block += 1) {
36
+ const blockStart = block * BLOCK_BYTES;
37
+ for (let i = 0; i < WORDS_PER_BLOCK; i += 1) schedule.setUint32(i * 4, padded.getUint32(blockStart + i * 4));
38
+ for (let i = WORDS_PER_BLOCK; i < ROUNDS; i += 1) {
39
+ const w3 = schedule.getUint32((i - 3) * 4);
40
+ const w8 = schedule.getUint32((i - 8) * 4);
41
+ const w14 = schedule.getUint32((i - 14) * 4);
42
+ const w16 = schedule.getUint32((i - 16) * 4);
43
+ schedule.setUint32(i * 4, rotateLeft(w3 ^ w8 ^ w14 ^ w16, 1));
44
+ }
45
+ let a = h0;
46
+ let b = h1;
47
+ let c = h2;
48
+ let d = h3;
49
+ let e = h4;
50
+ for (let i = 0; i < ROUNDS; i += 1) {
51
+ let f;
52
+ let k;
53
+ if (i < 20) {
54
+ f = b & c | ~b & d;
55
+ k = 1518500249;
56
+ } else if (i < 40) {
57
+ f = b ^ c ^ d;
58
+ k = 1859775393;
59
+ } else if (i < 60) {
60
+ f = b & c | b & d | c & d;
61
+ k = 2400959708;
62
+ } else {
63
+ f = b ^ c ^ d;
64
+ k = 3395469782;
65
+ }
66
+ const w = schedule.getUint32(i * 4);
67
+ const temp = rotateLeft(a, 5) + f + e + k + w >>> 0;
68
+ e = d;
69
+ d = c;
70
+ c = rotateLeft(b, 30);
71
+ b = a;
72
+ a = temp;
73
+ }
74
+ h0 = h0 + a >>> 0;
75
+ h1 = h1 + b >>> 0;
76
+ h2 = h2 + c >>> 0;
77
+ h3 = h3 + d >>> 0;
78
+ h4 = h4 + e >>> 0;
79
+ }
80
+ const digest = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(DIGEST_BYTES));
81
+ digest.setUint32(0, h0);
82
+ digest.setUint32(4, h1);
83
+ digest.setUint32(8, h2);
84
+ digest.setUint32(12, h3);
85
+ digest.setUint32(16, h4);
86
+ return new Uint8Array(digest.buffer);
87
+ }
88
+ //#endregion
89
+ exports.sha1 = sha1;
@@ -0,0 +1,4 @@
1
+ //#region src/crypto/sha1.d.ts
2
+ declare function sha1(message: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
3
+ //#endregion
4
+ export { sha1 };
@@ -0,0 +1,4 @@
1
+ //#region src/crypto/sha1.d.ts
2
+ declare function sha1(message: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
3
+ //#endregion
4
+ export { sha1 };
@@ -0,0 +1,88 @@
1
+ //#region src/crypto/sha1.ts
2
+ const BLOCK_BYTES = 64;
3
+ const DIGEST_BYTES = 20;
4
+ const ROUNDS = 80;
5
+ const WORDS_PER_BLOCK = 16;
6
+ const H0_INIT = 1732584193;
7
+ const H1_INIT = 4023233417;
8
+ const H2_INIT = 2562383102;
9
+ const H3_INIT = 271733878;
10
+ const H4_INIT = 3285377520;
11
+ function rotateLeft(value, bits) {
12
+ return (value << bits | value >>> 32 - bits) >>> 0;
13
+ }
14
+ function pad(message) {
15
+ const bitLength = message.length * 8;
16
+ const paddedLength = Math.ceil((message.length + 9) / BLOCK_BYTES) * BLOCK_BYTES;
17
+ const buffer = new ArrayBuffer(paddedLength);
18
+ const view = new DataView(buffer);
19
+ new Uint8Array(buffer).set(message, 0);
20
+ view.setUint8(message.length, 128);
21
+ view.setUint32(paddedLength - 8, 0);
22
+ view.setUint32(paddedLength - 4, bitLength >>> 0);
23
+ return view;
24
+ }
25
+ function sha1(message) {
26
+ const padded = pad(message);
27
+ const blockCount = padded.byteLength / BLOCK_BYTES;
28
+ let h0 = H0_INIT;
29
+ let h1 = H1_INIT;
30
+ let h2 = H2_INIT;
31
+ let h3 = H3_INIT;
32
+ let h4 = H4_INIT;
33
+ const schedule = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(320));
34
+ for (let block = 0; block < blockCount; block += 1) {
35
+ const blockStart = block * BLOCK_BYTES;
36
+ for (let i = 0; i < WORDS_PER_BLOCK; i += 1) schedule.setUint32(i * 4, padded.getUint32(blockStart + i * 4));
37
+ for (let i = WORDS_PER_BLOCK; i < ROUNDS; i += 1) {
38
+ const w3 = schedule.getUint32((i - 3) * 4);
39
+ const w8 = schedule.getUint32((i - 8) * 4);
40
+ const w14 = schedule.getUint32((i - 14) * 4);
41
+ const w16 = schedule.getUint32((i - 16) * 4);
42
+ schedule.setUint32(i * 4, rotateLeft(w3 ^ w8 ^ w14 ^ w16, 1));
43
+ }
44
+ let a = h0;
45
+ let b = h1;
46
+ let c = h2;
47
+ let d = h3;
48
+ let e = h4;
49
+ for (let i = 0; i < ROUNDS; i += 1) {
50
+ let f;
51
+ let k;
52
+ if (i < 20) {
53
+ f = b & c | ~b & d;
54
+ k = 1518500249;
55
+ } else if (i < 40) {
56
+ f = b ^ c ^ d;
57
+ k = 1859775393;
58
+ } else if (i < 60) {
59
+ f = b & c | b & d | c & d;
60
+ k = 2400959708;
61
+ } else {
62
+ f = b ^ c ^ d;
63
+ k = 3395469782;
64
+ }
65
+ const w = schedule.getUint32(i * 4);
66
+ const temp = rotateLeft(a, 5) + f + e + k + w >>> 0;
67
+ e = d;
68
+ d = c;
69
+ c = rotateLeft(b, 30);
70
+ b = a;
71
+ a = temp;
72
+ }
73
+ h0 = h0 + a >>> 0;
74
+ h1 = h1 + b >>> 0;
75
+ h2 = h2 + c >>> 0;
76
+ h3 = h3 + d >>> 0;
77
+ h4 = h4 + e >>> 0;
78
+ }
79
+ const digest = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(DIGEST_BYTES));
80
+ digest.setUint32(0, h0);
81
+ digest.setUint32(4, h1);
82
+ digest.setUint32(8, h2);
83
+ digest.setUint32(12, h3);
84
+ digest.setUint32(16, h4);
85
+ return new Uint8Array(digest.buffer);
86
+ }
87
+ //#endregion
88
+ export { sha1 };
package/dist/index.cjs CHANGED
@@ -4,8 +4,10 @@ const require_cfb_ole_package = require("./cfb/ole-package.cjs");
4
4
  const require_cfb_read = require("./cfb/read.cjs");
5
5
  const require_cfb_write = require("./cfb/write.cjs");
6
6
  const require_crypto_md5 = require("./crypto/md5.cjs");
7
+ const require_crypto_sha1 = require("./crypto/sha1.cjs");
7
8
  const require_crypto_rc4 = require("./crypto/rc4.cjs");
8
9
  const require_crypto_office_rc4 = require("./crypto/office-rc4.cjs");
10
+ const require_crypto_office_rc4_cryptoapi = require("./crypto/office-rc4-cryptoapi.cjs");
9
11
  const require_oleps_layout_metadata = require("./oleps/layout-metadata.cjs");
10
12
  const require_oleps_read = require("./oleps/read.cjs");
11
13
  const require_oleps_write = require("./oleps/write.cjs");
@@ -27,22 +29,30 @@ exports.OlePackageFormatError = require_cfb_ole_package.OlePackageFormatError;
27
29
  exports.OlePackageWriteError = require_cfb_ole_package.OlePackageWriteError;
28
30
  exports.PropertySetFormatError = require_oleps_read.PropertySetFormatError;
29
31
  exports.PropertySetWriteError = require_oleps_write.PropertySetWriteError;
32
+ exports.RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS = require_crypto_office_rc4_cryptoapi.RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS;
33
+ exports.RC4_CRYPTOAPI_SALT_LENGTH = require_crypto_office_rc4_cryptoapi.RC4_CRYPTOAPI_SALT_LENGTH;
34
+ exports.RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH = require_crypto_office_rc4_cryptoapi.RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH;
35
+ exports.RC4_CRYPTOAPI_VERIFIER_LENGTH = require_crypto_office_rc4_cryptoapi.RC4_CRYPTOAPI_VERIFIER_LENGTH;
30
36
  exports.decryptOfficeRc4 = require_crypto_office_rc4.decryptOfficeRc4;
31
37
  exports.deriveOfficeRc4BaseHash = require_crypto_office_rc4.deriveOfficeRc4BaseHash;
32
38
  exports.deriveOfficeRc4BlockKey = require_crypto_office_rc4.deriveOfficeRc4BlockKey;
39
+ exports.deriveRc4CryptoApiBlockKey = require_crypto_office_rc4_cryptoapi.deriveRc4CryptoApiBlockKey;
33
40
  exports.detectArchiveFormat = require_zip_detect.detectArchiveFormat;
34
41
  exports.hasSummaryInformationFields = require_oleps_layout_metadata.hasSummaryInformationFields;
35
42
  exports.isCompoundFile = require_cfb_detect.isCompoundFile;
36
43
  exports.isZipArchive = require_zip_detect.isZipArchive;
37
44
  exports.layoutMetadataToSummaryInformation = require_oleps_layout_metadata.layoutMetadataToSummaryInformation;
38
45
  exports.md5 = require_crypto_md5.md5;
46
+ exports.passwordToUtf16LeBytes = require_crypto_office_rc4.passwordToUtf16LeBytes;
39
47
  exports.rc4 = require_crypto_rc4.rc4;
40
48
  exports.readCompoundFile = require_cfb_read.readCompoundFile;
41
49
  exports.readOlePackage = require_cfb_ole_package.readOlePackage;
42
50
  exports.readPropertySetStream = require_oleps_read.readPropertySetStream;
43
51
  exports.readSummaryInformation = require_oleps_summary_information.readSummaryInformation;
52
+ exports.sha1 = require_crypto_sha1.sha1;
44
53
  exports.summaryInformationToLayoutMetadata = require_oleps_layout_metadata.summaryInformationToLayoutMetadata;
45
54
  exports.unzipPackage = require_zip_container.unzipPackage;
55
+ exports.verifyRc4CryptoApiPassword = require_crypto_office_rc4_cryptoapi.verifyRc4CryptoApiPassword;
46
56
  exports.walkArchive = require_zip_walk.walkArchive;
47
57
  exports.writeCompoundFile = require_cfb_write.writeCompoundFile;
48
58
  exports.writeOlePackage = require_cfb_ole_package.writeOlePackage;
package/dist/index.d.cts CHANGED
@@ -3,8 +3,10 @@ import { OlePackage, OlePackageFormatError, OlePackageWriteError, readOlePackage
3
3
  import { CompoundFileFormatError, CompoundFileStream, MAX_CFB_TOTAL_STREAM_BYTES, ReadCompoundFileOptions, readCompoundFile } from "./cfb/read.cjs";
4
4
  import { CompoundFileWriteError, WriteCompoundFileOptions, writeCompoundFile } from "./cfb/write.cjs";
5
5
  import { md5 } from "./crypto/md5.cjs";
6
- import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey } from "./crypto/office-rc4.cjs";
6
+ import { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword } from "./crypto/office-rc4-cryptoapi.cjs";
7
+ import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes } from "./crypto/office-rc4.cjs";
7
8
  import { rc4 } from "./crypto/rc4.cjs";
9
+ import { sha1 } from "./crypto/sha1.cjs";
8
10
  import { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream } from "./oleps/summary-information.cjs";
9
11
  import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.cjs";
10
12
  import { PropertySet, PropertyValue } from "./oleps/wire.cjs";
@@ -13,4 +15,4 @@ import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.cjs
13
15
  import { ZipEntry, unzipPackage, zipPackage } from "./zip/container.cjs";
14
16
  import { ArchiveFormat, detectArchiveFormat, isZipArchive } from "./zip/detect.cjs";
15
17
  import { ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, walkArchive } from "./zip/walk.cjs";
16
- export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackage, OlePackageFormatError, OlePackageWriteError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
18
+ export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackage, OlePackageFormatError, OlePackageWriteError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, deriveRc4CryptoApiBlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, passwordToUtf16LeBytes, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, sha1, summaryInformationToLayoutMetadata, unzipPackage, verifyRc4CryptoApiPassword, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
package/dist/index.d.ts CHANGED
@@ -3,8 +3,10 @@ import { OlePackage, OlePackageFormatError, OlePackageWriteError, readOlePackage
3
3
  import { CompoundFileFormatError, CompoundFileStream, MAX_CFB_TOTAL_STREAM_BYTES, ReadCompoundFileOptions, readCompoundFile } from "./cfb/read.js";
4
4
  import { CompoundFileWriteError, WriteCompoundFileOptions, writeCompoundFile } from "./cfb/write.js";
5
5
  import { md5 } from "./crypto/md5.js";
6
- import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey } from "./crypto/office-rc4.js";
6
+ import { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword } from "./crypto/office-rc4-cryptoapi.js";
7
+ import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes } from "./crypto/office-rc4.js";
7
8
  import { rc4 } from "./crypto/rc4.js";
9
+ import { sha1 } from "./crypto/sha1.js";
8
10
  import { FMTID_SUMMARY_INFORMATION, SummaryInformationProperties, readSummaryInformation, writeSummaryInformationStream } from "./oleps/summary-information.js";
9
11
  import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.js";
10
12
  import { PropertySet, PropertyValue } from "./oleps/wire.js";
@@ -13,4 +15,4 @@ import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.js"
13
15
  import { ZipEntry, unzipPackage, zipPackage } from "./zip/container.js";
14
16
  import { ArchiveFormat, detectArchiveFormat, isZipArchive } from "./zip/detect.js";
15
17
  import { ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, walkArchive } from "./zip/walk.js";
16
- export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackage, OlePackageFormatError, OlePackageWriteError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
18
+ export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileStream, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackage, OlePackageFormatError, OlePackageWriteError, type PropertySet, PropertySetFormatError, PropertySetWriteError, type PropertyValue, RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, ReadCompoundFileOptions, SummaryInformationProperties, WalkArchiveOptions, WriteCompoundFileOptions, ZipEntry, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, deriveRc4CryptoApiBlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, passwordToUtf16LeBytes, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, sha1, summaryInformationToLayoutMetadata, unzipPackage, verifyRc4CryptoApiPassword, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@ import { OlePackageFormatError, OlePackageWriteError, readOlePackage, writeOlePa
3
3
  import { CompoundFileFormatError, MAX_CFB_TOTAL_STREAM_BYTES, readCompoundFile } from "./cfb/read.js";
4
4
  import { CompoundFileWriteError, writeCompoundFile } from "./cfb/write.js";
5
5
  import { md5 } from "./crypto/md5.js";
6
+ import { sha1 } from "./crypto/sha1.js";
6
7
  import { rc4 } from "./crypto/rc4.js";
7
- import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey } from "./crypto/office-rc4.js";
8
+ import { OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, passwordToUtf16LeBytes } from "./crypto/office-rc4.js";
9
+ import { RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, deriveRc4CryptoApiBlockKey, verifyRc4CryptoApiPassword } from "./crypto/office-rc4-cryptoapi.js";
8
10
  import { hasSummaryInformationFields, layoutMetadataToSummaryInformation, summaryInformationToLayoutMetadata } from "./oleps/layout-metadata.js";
9
11
  import { PropertySetFormatError, readPropertySetStream } from "./oleps/read.js";
10
12
  import { PropertySetWriteError, writePropertySetStream } from "./oleps/write.js";
@@ -12,4 +14,4 @@ import { FMTID_SUMMARY_INFORMATION, readSummaryInformation, writeSummaryInformat
12
14
  import { unzipPackage, zipPackage } from "./zip/container.js";
13
15
  import { detectArchiveFormat, isZipArchive } from "./zip/detect.js";
14
16
  import { ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, walkArchive } from "./zip/walk.js";
15
- export { ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackageFormatError, OlePackageWriteError, PropertySetFormatError, PropertySetWriteError, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, summaryInformationToLayoutMetadata, unzipPackage, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
17
+ export { ArchiveWalkLimitError, CompoundFileFormatError, CompoundFileWriteError, FMTID_SUMMARY_INFORMATION, MAX_CFB_TOTAL_STREAM_BYTES, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, OFFICE_RC4_BLOCK_SIZE, OFFICE_RC4_DOC_BLOCK_SIZE, OFFICE_RC4_VERIFIER_LENGTH, OlePackageFormatError, OlePackageWriteError, PropertySetFormatError, PropertySetWriteError, RC4_CRYPTOAPI_DEFAULT_KEY_SIZE_BITS, RC4_CRYPTOAPI_SALT_LENGTH, RC4_CRYPTOAPI_VERIFIER_HASH_LENGTH, RC4_CRYPTOAPI_VERIFIER_LENGTH, decryptOfficeRc4, deriveOfficeRc4BaseHash, deriveOfficeRc4BlockKey, deriveRc4CryptoApiBlockKey, detectArchiveFormat, hasSummaryInformationFields, isCompoundFile, isZipArchive, layoutMetadataToSummaryInformation, md5, passwordToUtf16LeBytes, rc4, readCompoundFile, readOlePackage, readPropertySetStream, readSummaryInformation, sha1, summaryInformationToLayoutMetadata, unzipPackage, verifyRc4CryptoApiPassword, walkArchive, writeCompoundFile, writeOlePackage, writePropertySetStream, writeSummaryInformationStream, zipPackage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archive-codec",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "ZIP-in-ZIP recursive walking with depth and cumulative decompressed-size guards, bounded classic OLE compound-file ([MS-CFB]) reading and writing, and [MS-OLEPS] Property Set Stream reading and writing - zero document-format knowledge, the archive and container utility package for the documents.js family.",
5
5
  "type": "module",
6
6
  "repository": {