Socket
Socket
Sign inDemoInstall

officecrypto-tool

Package Overview
Dependencies
6
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.2 to 0.0.3

.github/dependabot.yml

6

index.d.ts

@@ -19,2 +19,8 @@

password: string;
/**
* @desc Encryption Type
* optional
*/
type?: 'standard';
}

@@ -21,0 +27,0 @@

31

index.js

@@ -17,3 +17,10 @@ /* eslint-disable valid-jsdoc */

async function decrypt(input, options) {
if (!Buffer.isBuffer(input)) throw new Error('The input must be a buffer');
if (!Buffer.isBuffer(input)) {
// This is an ArrayBuffer in the browser. Convert to a Buffer.
if (ArrayBuffer.isView(input)) {
input = Buffer.from(input);
} else {
throw new Error('The input must be a buffer');
}
}
if (!options || !options.password) throw new Error('options.password is required');

@@ -24,3 +31,3 @@

if (encryptionInfo) { // 这个是xlsx格式的加密
if (encryptionInfo) { // This is encrypted in xlsx format
const encryptedPackage = CFB.find(cfb, '/EncryptedPackage');

@@ -69,3 +76,10 @@ const einfo = common.parseEncryptionInfo(encryptionInfo.content);

function encrypt(input, options) {
if (!Buffer.isBuffer(input)) throw new Error('The input must be a buffer');
if (!Buffer.isBuffer(input)) {
// This is an ArrayBuffer in the browser. Convert to a Buffer.
if (ArrayBuffer.isView(input)) {
input = Buffer.from(input);
} else {
throw new Error('The input must be a buffer');
}
}
if (!options || !options.password) throw new Error('options.password is required');

@@ -75,3 +89,12 @@

if (options.password.length > maxFieldLength) throw new Error(`The maximum password length is ${maxFieldLength}`);
const output = ecma376Agile.encrypt(input, options.password);
let output;
if (options.hasOwnProperty('type') && !['standard'].includes(options.type)) {
throw new Error(`options.type must be ['standard']`);
}
if (options.type === 'standard') {
output = ecma376Standard.encryptStandard(input, options.password);
} else {
output = ecma376Agile.encrypt(input, options.password);
}
return output;

@@ -78,0 +101,0 @@ }

3

package.json
{
"name": "officecrypto-tool",
"version": "0.0.2",
"version": "0.0.3",
"description": "officeCrypto is a library for node.js that can be used to decrypt and encrypt excel files.",

@@ -15,2 +15,3 @@ "keywords": [

"private": false,
"license": "MIT",
"author": "zurmokeeper",

@@ -17,0 +18,0 @@ "main": "index.js",

@@ -5,4 +5,5 @@ /* eslint-disable require-jsdoc */

const crypto = require('crypto');
const cfb = require('cfb');
exports.convertPasswordToKey = function convertPasswordToKey(password, algId, algIdHash, providerType, keySize, saltSize, salt) {
const convertPasswordToKey = exports.convertPasswordToKey = function convertPasswordToKey(password, algId, algIdHash, providerType, keySize, saltSize, salt) {
const ITER_COUNT = 50000;

@@ -46,2 +47,6 @@ const cbRequiredKeyLength = keySize / 8;

exports.verifyKey = function verifyKey(key, encryptedVerifier, encryptedVerifierHash) {
// In the browser environment. Convert to a Buffer.
if (!Buffer.isBuffer(encryptedVerifier)) encryptedVerifier = Buffer.from(encryptedVerifier);
if (!Buffer.isBuffer(encryptedVerifierHash)) encryptedVerifierHash = Buffer.from(encryptedVerifierHash);
const aes = crypto.createDecipheriv('aes-128-ecb', key, Buffer.alloc(0));

@@ -64,2 +69,5 @@ aes.setAutoPadding(false);

// In the browser environment. Convert to a Buffer.
if (!Buffer.isBuffer(input)) input = Buffer.from(input);
// The package is encoded in chunks. Encrypt/decrypt each and concat.

@@ -94,3 +102,3 @@ let start = 0; let end = 0;

exports.encrypt = function encrypt(key, input) {
const encrypt = exports.encrypt = function encrypt(key, input) {
const outputChunks = [];

@@ -122,3 +130,2 @@ const offset = 0;

// Concat all of the output chunks.

@@ -138,1 +145,89 @@ let output = Buffer.concat(outputChunks);

}
function genVerifier(key) {
const verifierHashInput = crypto.randomBytes(16);
const aes = crypto.createCipheriv('aes-128-ecb', key, Buffer.alloc(0));
aes.setAutoPadding(false);
const verifierHashInputValue = Buffer.concat([aes.update(verifierHashInput), aes.final()]);
let verifierHashInputKey = crypto.createHash('sha1').update(verifierHashInput).digest();
const blockSize = 16;
const remainder = verifierHashInputKey.length % blockSize;
if (remainder) verifierHashInputKey = Buffer.concat([verifierHashInputKey, Buffer.alloc(blockSize - remainder)]);
const aes2 = crypto.createCipheriv('aes-128-ecb', key, Buffer.alloc(0));
aes2.setAutoPadding(false);
const verifierHashInputKeyValue = Buffer.concat([aes2.update(verifierHashInputKey), aes2.final()]);
return {encryptedVerifier: verifierHashInputValue, encryptedVerifierHash: verifierHashInputKeyValue};
}
function buildEncryptionInfo(key, keyDataSaltValue) {
const blob = Buffer.alloc(224);
cfb.utils.prep_blob(blob, 0);
blob.write_shift(2, 0x0004);
blob.write_shift(2, 0x0002);
blob.write_shift(4, 0x24); // EncryptionHeaderFlags
blob.write_shift(4, 0x8c); // 140 EncryptionHeaderSize
blob.write_shift(4, 0x24); // Flags
blob.write_shift(4, 0x00); // SizeExtra
blob.write_shift(4, 0x660E); // AlgID
blob.write_shift(4, 0x8004); // AlgIDHash
blob.write_shift(4, 0x80); // KeySize 128
blob.write_shift(4, 0x18); // ProviderType;
blob.write_shift(4, 0x00); // Reserved1
blob.write_shift(4, 0x00); // Reserved2
// The entire EncryptionHeaderSize is 140 bytes, the above is already 32 bytes, leaving 108 bytes, since it is utf16le
// so providerName = 108/2 = 54
const providerName = 'Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)';
blob.write_shift(54, providerName, 'utf16le');
blob.write_shift(4, 0x10); // SaltSize
const {encryptedVerifier, encryptedVerifierHash} = genVerifier(key);
blob.write_shift(16, keyDataSaltValue.toString('hex'), 'hex'); // Salt
blob.write_shift(16, encryptedVerifier.toString('hex'), 'hex'); // EncryptedVerifier
blob.write_shift(4, 0x14); // VerifierHashSize
blob.write_shift(32, encryptedVerifierHash.toString('hex'), 'hex'); // EncryptedVerifierHash
return blob;
}
function buildEncryptionPackage(key, input) {
const output = encrypt(key, input);
return output;
}
exports.encryptStandard = function encryptStandard(input, password) {
// Create a new CFB
let output = cfb.utils.cfb_new();
const KeySize = 128;
const AlgID = 0x660E;
const AlgIDHash = 0x8004;
const ProviderType = 0x18;
const saltSize = 16;
const keyDataSaltValue = crypto.randomBytes(16);
const key = convertPasswordToKey(password, AlgID, AlgIDHash, ProviderType, KeySize, saltSize, keyDataSaltValue);
const encryptionInfoBuffer = buildEncryptionInfo(key, keyDataSaltValue);
const encryptedPackage = buildEncryptionPackage(key, input);
// Add the encryption info and encrypted package
cfb.utils.cfb_add(output, 'EncryptionInfo', encryptionInfoBuffer);
cfb.utils.cfb_add(output, 'EncryptedPackage', encryptedPackage);
// Delete the SheetJS entry that is added at initialization
cfb.utils.cfb_del(output, '\u0001Sh33tJ5');
// Write to a buffer and return
output = cfb.write(output);
// The cfb library writes to a Uint8array in the browser. Convert to a Buffer.
if (!Buffer.isBuffer(output)) output = Buffer.from(output);
return output;
};

@@ -7,8 +7,34 @@

const filePath = './tests/data/decrypt';
const filePath = './tests/data/encrypt';
describe('ecma376_standard encrypt', () => {
it('encrypt', async () => {
expect(200).toEqual(200);
const input = await fs.readFile(`${filePath}/standard_wait_for_encrypt.xlsx`);
const output = officeCrypto.encrypt(input, {password: '123456', type: 'standard'});
await fs.writeFile(`${filePath}/standard_encrypt_finish.xlsx`, output);
// expect(200).toEqual(200);
});
it(`options.type must be ['standard'], options.type= ''`, async () => {
const test = function test() {
return async function() {
const input = await fs.readFile(`${filePath}/standard_wait_for_encrypt.xlsx`);
const output = officeCrypto.encrypt(input, {password: '123456', type: ''});
};
};
await expect(test()).rejects.toThrowError(new Error( `options.type must be ['standard']` ));
});
it(`options.type must be ['standard'], options.type= 'xx'`, async () => {
const test = function test() {
return async function() {
const input = await fs.readFile(`${filePath}/standard_wait_for_encrypt.xlsx`);
const output = officeCrypto.encrypt(input, {password: '123456', type: 'xx'});
};
};
await expect(test()).rejects.toThrowError(new Error( `options.type must be ['standard']` ));
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc