Skip to content

Instantly share code, notes, and snippets.

@alichtman
Last active March 8, 2020 13:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alichtman/0ecc89dab1fe47d635e65cb40a5dce0e to your computer and use it in GitHub Desktop.
Save alichtman/0ecc89dab1fe47d635e65cb40a5dce0e to your computer and use it in GitHub Desktop.
/**
* BUG: When encrypting certain types of files, an empty (0B) file is created.
* I can reproduce it with PDFs and JPGs, however, this works with text
* files and some executables, so it's not a "plaintext-only" issue.
**/
const fs = require("fs");
const crypto = require("crypto");
/**
* Returns a SHA512 digest to be used as the key for AES encryption. Uses a 64 byte salt with 10,000 iterations of PBKDF2
* Follows the NIST standards described here: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
* @param {Buffer} salt 16 byte random salt
* @param {string} encryptionKey User's entered encryption key
* @return {Buffer} SHA512 hash that will be used as the IV.
*/
function createDerivedKey(salt, encryptionKey) {
return crypto.pbkdf2Sync(
encryptionKey,
salt,
(iterations = 10000),
(keylen = 32),
(digest = "sha512")
);
}
/**
* Encrypts a file using this format:
* (https://gist.github.com/AndiDittrich/4629e7db04819244e843)
* +--------------------+-----------------------+----------------+----------------+
* | Salt | Initialization Vector | Auth Tag | Payload |
* | Used to derive key | AES GCM XOR Init | Data Integrity | Encrypted File |
* | 64 Bytes, random | 16 Bytes, random | 16 Bytes | (N-96) Bytes |
* +--------------------+-----------------------+----------------+----------------+
*
* A huge thank you to: https://medium.com/@brandonstilson/lets-encrypt-files-with-node-85037bea8c0e
*
* @param {String} filePath Absolute path of unencrypted file.
* @param {String} encryptionKey User verified encryption key.
* @return {String} Absolute path of encrypted file.
*/
function encryptFile(filePath, encryptionKey) {
console.log(`Encrypting ${filePath} with key: ${encryptionKey}`);
// Create cipher
const salt = crypto.randomBytes(64);
const derivedKey = createDerivedKey(salt, encryptionKey);
const initializationVector = crypto.randomBytes(16);
let cipher = crypto.createCipheriv(
"aes-256-gcm",
derivedKey,
initializationVector
);
let encryptedFilePath = `${filePath}.qlock`;
let write = fs.createWriteStream(encryptedFilePath);
let encryptBlob = fs
.createReadStream(filePath)
.pipe(cipher)
.on("finish", () => {
encryptBlob
.pipe(write)
.on("error", () => { console.log("Write error.")});
});
return encryptedFilePath;
}
/**************
* Entry Points
**************/
/**
* QuickLock file encryption process.
* @param {String} filePath Absolute path of file.
* @param {String} encryptionPhrase Passphrase for encryption that has been verified by the user already in the GUI.
* @return {String} Absolute path of encrypted file.
*/
function onFileEncryptRequest(filePath, encryptionPhrase) {
let encryptedFilePath = encryptFile(filePath, encryptionPhrase);
console.log(`Encrypted file: ${encryptedFilePath}`);
return encryptedFilePath;
}
function testing_main() {
// let file = "/Users/alichtman/scratchpad/05ap65wxn5c01.jpg";
let file = "/Users/alichtman/scratchpad/test.txt";
// let file = "/Users/alichtman/scratchpad/a.out";
// let file = "/Users/alichtman/scratchpad/hacking-ciphers-with-python.pdf";
let encryptedFilePath = onFileEncryptRequest(
file,
"test"
)
}
testing_main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment