Skip to content

Instantly share code, notes, and snippets.

@kamronbatman
Last active January 29, 2019 07:31
Show Gist options
  • Save kamronbatman/b0e7fc9a091f33a424d3cc50d047de41 to your computer and use it in GitHub Desktop.
Save kamronbatman/b0e7fc9a091f33a424d3cc50d047de41 to your computer and use it in GitHub Desktop.
Compresses and encrypts text files for exchanging credentials, private keys, and environment variables using Node.
#!/usr/bin/env node
const { promisify } = require('util');
/* eslint-disable import/no-extraneous-dependencies */
const glob = promisify(require('glob'));
/* eslint-enable */
const cryptFile = require('../util/cryptFile');
const handler = async (command, secret, ...filePathArgs) => {
const files = (await Promise.all(
filePathArgs.map(pattern => glob(pattern)),
)).reduce((acc, paths) => [...acc, ...paths], []);
const results = await cryptFile({ command, secret, files });
results.forEach(({ error, path }) => {
if (error) { console.log(error, path); }
});
console.log('Completed', command === 'encrypt' ? 'encrypting' : 'decrypting', 'files');
};
handler(...process.argv.slice(2));
const fs = require('fs');
const { promisify } = require('util');
const crypto = require('crypto');
const zlib = require('zlib');
const sha1DigestStream = require('./sha1DigestStream');
const read = promisify(fs.read);
const openFile = promisify(fs.open);
const defaultEncryptHeader = 'AES256';
const algorithm = 'aes-256-cbc-hmac-sha256';
const isEncrypted = buffer => buffer.toString() === defaultEncryptHeader;
const handler = async ({ command = 'encrypt', secret, files }) => {
const toEncrypt = command === 'encrypt';
const headerLength = Buffer.byteLength(defaultEncryptHeader, 'utf8');
const totalHeaderLength = headerLength + 16 + 20; // AES + IV + HASH
const readOptions = { start: toEncrypt ? 0 : totalHeaderLength };
const key = crypto.createHash('sha256').update(secret).digest();
return Promise.all(files.map(async (file) => {
const outputPath = `${file}.tmp`;
const ws = fs.createWriteStream(outputPath);
const cryptCipher = toEncrypt ? crypto.createCipheriv : crypto.createDecipheriv;
let iv;
let hash;
if (toEncrypt) {
iv = crypto.randomBytes(16);
ws.write(Buffer.from(defaultEncryptHeader, 'utf8'));
ws.write(iv);
} else {
const fd = await openFile(file, 'r');
const { buffer } = await read(fd, Buffer.alloc(totalHeaderLength), 0, totalHeaderLength, 0);
if (!isEncrypted(buffer.slice(0, headerLength))) {
fs.unlinkSync(outputPath);
return { error: `Skipped: ${file} is not encrypted`, path: file };
}
iv = buffer.slice(headerLength, headerLength + 16);
hash = buffer.slice(headerLength + 16);
}
return new Promise((resolve) => {
if (toEncrypt) {
const fileStream = fs.createReadStream(file, readOptions)
.pipe(crypto.createHash('sha1'));
fileStream.pipe(ws, { end: false });
fileStream.on('finish', () => {
fs.createReadStream(file, readOptions)
.pipe(zlib.createGzip({ level: 9 }))
.pipe(cryptCipher(algorithm, key, iv))
.pipe(ws)
.on('close', () => {
fs.renameSync(outputPath, file);
resolve({ path: file });
});
});
return;
}
const hashDigest = Buffer.alloc(20, 0);
fs.createReadStream(file, readOptions)
.pipe(cryptCipher(algorithm, key, iv))
.pipe(zlib.createGunzip())
.pipe(sha1DigestStream(hashDigest))
.pipe(ws)
.on('close', () => {
if (!hash.equals(Buffer.from(hashDigest))) {
fs.unlinkSync(outputPath);
resolve({ error: 'Failed to decrypt', path: file });
return;
}
fs.renameSync(outputPath, file);
resolve({ path: file });
});
});
}));
};
module.exports = handler;
const crypto = require('crypto');
const { Transform } = require('stream');
const sha1DigestStream = (outputDigest) => {
const hash = crypto.createHash('sha1');
const ts = new Transform({
read() {},
write(chunk, encoding, next) {
hash.update(chunk);
this.push(chunk, encoding);
next();
},
end(chunk, encoding) {
hash.update(chunk);
this.push(chunk, encoding);
},
});
ts.on('finish', () => {
hash.digest().copy(outputDigest, 0);
});
return ts;
};
module.exports = sha1DigestStream;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment