dtjohnson / xlsx-populate

Excel XLSX parser/generator written in JavaScript with Node.js and browser support, jQuery/d3-style method chaining, encryption, and a focus on keeping existing workbook features and styles in tact.
MIT License
954 stars 182 forks source link

Feature Request: Lock/Unlock Sheets #72

Open alandoyle95 opened 7 years ago

alandoyle95 commented 7 years ago

I think it would be nice to be able to generate a locked sheet and might as well toss in unlocking at the same time..

NOTE: SHEETS, not workbook! unless that is easy to do as well...

dtjohnson commented 7 years ago

Should be doable. Locking a sheet results in XML that looks like this:

<sheetProtection algorithmName="SHA-512" hashValue="K3N1C3KV8RviRgOLCmtgPRHkMdjOp07JmELoFifhJJ6aaud2jQc/orpoY/cdaJ6PEPSRGTWmXadgyOvkaGafqA==" saltValue="7TSP09OHP6wu66l+FqI/bw==" spinCount="100000" sheet="1" objects="1" scenarios="1"/>

We'd need to research the OOXML docs on this as it seems non-trivial.

LesterLyu commented 5 years ago

@dtjohnson I found out how to verify the password: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f

    /**
     * Verify the sheet protection password.
     * @param {string} password - The password.
     * @param {string} algorithm - The hash algorithm.
     * @param {string} salt - Base64 encoded salt.
     * @param {number} spinCount - The spinCount, must less than 10,000,000.
     * @param {string} expectedHash - Base64 encoded hash.
     * @return {boolean} If the password correct.
     */
    verifySheetPassword(password, algorithm, salt, spinCount, expectedHash) {
        const passwordBuffer = Buffer.from(password, 'utf16le');
        const saltBuffer = Buffer.from(salt, 'base64');
        let hash = this._hash(algorithm, saltBuffer, passwordBuffer);
        for (let i = 0; i < spinCount; i++) {
            const indexBuffer = this._createUInt32LEBuffer(i);
            hash = this._hash(algorithm, hash, indexBuffer);
        }
        return expectedHash === hash.toString('base64');
    }