import { OleFileIO } from './olefile.cjs'; export { isOleFile } from './olefile.cjs'; /** * Base interface every OfficeFile implementation conforms to. */ interface LoadKeyOptions { password?: string; privateKey?: Uint8Array | string; secretKey?: Uint8Array; verifyPassword?: boolean; } interface DecryptOptions { verifyIntegrity?: boolean; } interface BaseOfficeFile { format: string; keyTypes: readonly string[]; loadKey(opts: LoadKeyOptions): void; decrypt(opts?: DecryptOptions): Uint8Array; isEncrypted(): boolean; } declare class FileFormatError extends Error { constructor(message: string); } declare class ParseError extends Error { constructor(message: string); } declare class DecryptionError extends Error { constructor(message: string); } declare class EncryptionError extends Error { constructor(message: string); } declare class InvalidKeyError extends DecryptionError { constructor(message: string); } /** * OOXML (DOCX/XLSX/PPTX) format handler. * * Encrypted OOXML is wrapped inside an OLE compound file containing an * `EncryptionInfo` stream (header + XML descriptor or binary header) and an * `EncryptedPackage` stream (the actual encrypted ZIP). * * Plain OOXML is a regular ZIP starting with `PK\x03\x04`. * * Direct port of `msoffcrypto/format/ooxml.py`. */ /** Heuristic: is this a plain (unencrypted) OOXML file? */ declare function isOoxml(buf: Uint8Array): boolean; declare class OOXMLFile implements BaseOfficeFile { format: "ooxml"; keyTypes: readonly string[]; type: "agile" | "standard" | "plain"; private file; private info?; private secretKey; constructor(buf: Uint8Array); loadKey(opts: LoadKeyOptions): void; decrypt(opts?: DecryptOptions): Uint8Array; isEncrypted(): boolean; } /** * Excel 97-2003 (BIFF8) format handler. * * The Workbook stream is decrypted in place — encryption is applied per * record, but a handful of records (BOF, FilePass, BoundSheet8.lbPlyPos, …) * MUST stay plaintext. We mirror the Python implementation's two-pass plan: * 1. Walk all records; build a per-byte plan ("keep plain" / "decrypt"). * 2. Build a contiguous "encrypted-only" buffer (zeros where plain bytes * should land), feed it to the cipher, then merge back per the plan. * * Direct port of `msoffcrypto/format/xls97.py`. */ declare class Xls97File implements BaseOfficeFile { ole: OleFileIO; format: string; keyTypes: readonly string[]; private workbookData; private type?; private password?; private salt?; private keySize?; constructor(ole: OleFileIO); loadKey(opts: LoadKeyOptions): void; decrypt(_opts?: DecryptOptions): Uint8Array; isEncrypted(): boolean; } /** * Word 97-2003 (BIFF / FIB) format handler. * * Word's encryption affects three streams: * - WordDocument: starts with a 0x44-byte FibBase header. The first 0x44 * bytes (with `fEncrypted=0` and `fObfuscation=0` cleared) MUST be * written plaintext; the rest of the stream is decrypted. * - 0Table or 1Table (selected by FibBase.fWhichTblStm): fully decrypted. * - Data: optional, fully decrypted if present. * * Direct port of `msoffcrypto/format/doc97.py`. */ declare class Doc97File implements BaseOfficeFile { ole: OleFileIO; format: string; keyTypes: readonly string[]; private fib; private tableName; private type?; private password?; private salt?; private keySize?; constructor(ole: OleFileIO); loadKey(opts: LoadKeyOptions): void; decrypt(_opts?: DecryptOptions): Uint8Array; private cipherDecrypt; isEncrypted(): boolean; } /** * PowerPoint 97-2003 format handler. * * PPT's encryption story is the most involved of the legacy formats: * * 1. The Current User Stream contains a CurrentUserAtom whose * `offsetToCurrentEdit` points into the PowerPoint Document stream. * 2. Following that pointer lands on a UserEditAtom; its * `encryptSessionPersistIdRef` resolves (via the persist object * directory built from the chain of UserEditAtoms + * PersistDirectoryAtoms) to a CryptSession10Container record holding * the EncryptionInfo header. * 3. Each persist object in the directory is independently RC4-CryptoAPI * encrypted, using its `persistId` as the cipher block index. * 4. UserEditAtom, PersistDirectoryAtom, and CryptSession10Container * records themselves MUST NOT be encrypted. * * We mirror the Python implementation: build the persist directory, decrypt * each persist object in place, zero out the CryptSession10Container, drop * the encryptSessionPersistIdRef, and rewrite Current User Stream's header * token to "not encrypted". * * Direct port of `msoffcrypto/format/ppt97.py`. */ declare class Ppt97File implements BaseOfficeFile { ole: OleFileIO; format: string; keyTypes: readonly string[]; private currentUserBytes; private pptBytes; private password?; private salt?; private keySize?; constructor(ole: OleFileIO); loadKey(opts: LoadKeyOptions): void; decrypt(_opts?: DecryptOptions): Uint8Array; isEncrypted(): boolean; } /** * office-crypto — TypeScript port of msoffcrypto-tool. * * Entry point. Exposes: * - `OfficeFile(buf)`: factory that auto-detects file format. * - `OOXMLFile`: handler for DOCX/XLSX/PPTX. * - `Xls97File` / `Doc97File` / `Ppt97File`: legacy stubs. * - `isEncrypted(buf)`: quick helper. * * See README for usage and the documented public surface area. */ /** * Auto-detect the format of `buf` and return the appropriate handler. * * @example * const buf = await fs.promises.readFile("encrypted.docx"); * const file = OfficeFile(buf); * file.loadKey({ password: "secret" }); * const decrypted = file.decrypt(); * await fs.promises.writeFile("plain.docx", decrypted); */ declare function OfficeFile(buf: Uint8Array | ArrayBuffer): BaseOfficeFile; /** * Returns true if the input bytes look like an encrypted Office file. * Plain OOXML (.docx etc.) returns false; legacy OLE-based protected files * return true if a known encryption marker is present. */ declare function isEncrypted(buf: Uint8Array | ArrayBuffer): boolean; export { type BaseOfficeFile, type DecryptOptions, DecryptionError, Doc97File, EncryptionError, FileFormatError, InvalidKeyError, type LoadKeyOptions, OOXMLFile, OfficeFile, OleFileIO, ParseError, Ppt97File, Xls97File, isEncrypted, isOoxml };