How to close node decipher stream on error - node.js

I'm trying to close a decipher stream created with Crypto.createDecipheriv when the readable stream feeding it errors. I am doing this per the recommendation in the docs: "One important caveat is that if the Readable stream emits an error during processing, the Writable destination is not closed automatically. If an error occurs, it will be necessary to manually close each stream in order to prevent memory leaks." https://nodejs.org/dist/latest-v6.x/docs/api/stream.html#stream_readable_read_size. When I call the end() function on the decrypt stream I get an error: Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
. How can I close the stream and avoid this error? Here is a full code sample:
const Fs = require('fs');
const Crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const Iv = Crypto.randomBytes(16);
const key = Crypto.randomBytes(32);
const readStream = Fs.createReadStream('./image1.jpg');
const writeStream = Fs.createWriteStream('./out.file');
const encrypt = Crypto.createCipheriv(algorithm, key, Iv);
writeStream.on('finish', () => {
const readStream2 = Fs.createReadStream('./out.file');
const writeStream2 = Fs.createWriteStream('./out2.jpeg');
const decrypt = Crypto.createDecipheriv(algorithm, key, Iv);
writeStream2.on('finish', () => {
console.log('fin');
});
readStream2.on('error', (err) => {
console.error(err.message);
decrypt.end();
writeStream2.end();
});
readStream2.pipe(decrypt).pipe(writeStream2);
readStream2.emit('error', new Error('Oh Fuck'));
});
readStream.pipe(encrypt).pipe(writeStream);

Related

Extracting initVect from node stream a deciphering the rest

I have readableStream with an encrypted file and I would like to decrypt it. So far I was able to create 2 readableStreams - with 1 I extracted the IV and the second was used to decrypt the rest of the data, but I would like to just pipe it into one stream - both IV extraction AND the decryption. Something like this:
function getDecryptionStream(password, initVectLength = 16) {
const cipherKey = getCipherKey(password);
const unzip = zlib.createUnzip();
return new Transform({
transform(chunk, encoding, callback) {
if (!this.initVect) {
this.initVect = chunk.subarray(0, initVectLength);
chunk = chunk.subarray(initVectLength, chunk.length);
}
const decipher = crypto.createDecipheriv(algorithm, cipherKey, this.initVect);
callback(null, chunk.pipe(decipher).pipe(unzip));
}
});
}
function decrypt({ file, newFile, passphrase }) {
const readStream = fs.createReadStream(file);
const writeStream = fs.createWriteStream(newFile);
const decryptStream = getDecryptionStream(passphrase , IV_LENGTH);
readStream
.pipe(decryptStream)
.pipe(writeStream);
}
However I cannot figure out, how to process the chunk as chunk.pipe(decipher) throws an error. TypeError: chunk.pipe is not a function as it as Buffer

Nodejs encrypt and decrypt using crypto decrypt badly only firsts characters

I have a js script that crypt and decrypt a file using these two functions
Encryption
function encryptFile(fileName) {
const input = fs.createReadStream(fileName);
const output = fs.createWriteStream(fileName + '.crypt');
var aes = crypto.createCipheriv(algorithm, Buffer.from(key), initVect);
input
.pipe(aes)
.pipe(output)
.on('finish', function () {
console.log('FILE ENCRYPTED!');
}).on('error', error => {
console.log(error);
});
}
Decryption
function decryptFile(fileName) {
const input = fs.createReadStream(fileName);
const output = fs.createWriteStream(fileName + '.clear');
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), initVect);
input
.pipe(decipher)
.pipe(output)
.on('finish', () => {
console.log('FILE DECRYPTED!');
})
.on('error', error => {
console.log(error);
});
}
Some assumption:
I use aes-256-cbc as algorithm
key is a 32 char string
initVect is a crypto.randomBytes(16);
Using these two function to crypt and decrypt a file I obtain a bad decryption. No error while execution but when I open the decrypted file the firsts 9 characters is still encrypted. Indeed, if I insert at the begin of my file 9 random charachters, the whole content of my file was decrypted correctly but there are the firsts chars encrypted.
Any solution?
UPDATE
I also use these two function passing them the content of the file
Encryption
function encrypt(text) {
const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), initVect);
let encrypted = cipher.update(text, 'utf8', 'base64')
encrypted += cipher.final('base64');
return encrypted;
};
Decryption
function decrypt(crypted_text) {
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), initVect);
let decrypted = decipher.update(crypted_text, 'base64');
decrypted += decipher.final();
return decrypted;
}
Using simple string these function work fine, saving the crypted (return of encrypt()) in a file, reading it and decrypt it the result decrypted (return of decrypt()) has the same problem.

Saving object to JSON file than using it back in function after reading

I am completly new to node.js but have to use it in my student project that require this library: https://github.com/bitchan/eccrypto.
The goal is to encrypt file using ethereum public key, save it as JSON and than read it back to decrypt with private key:
var fs = require('fs');
var crypto = require("crypto");
var eccrypto = require("eccrypto");
var content = fs.readFileSync('pdf_test.pdf');
var importPrivateKey = "c337ded6f56c07205fb7b391654d7d463c9e0c726869523ae6024c9bec878878"
var importPublicKey = "04730a151f545f5dcdb1c6d99fb1251f5c70f216f39ba2681bcf10db16bd582e6720bc881d51f25ffbe961df6a0af24a9d39a4db3d86a7f6b3f9bf4eaac0e4006b"
var privateKey = new Buffer(importPrivateKey, "hex");
var publicKey = new Buffer(importPublicKey, "hex");
// Encrypting the file for B.
eccrypto.encrypt(publicKey, Buffer(content)).then(function(encrypted) {
//console.log('Encrypted message ' + JSON.stringify(encrypted));
let data = JSON.stringify(encrypted);
fs.writeFileSync('encrypted.json', data);
console.log('encryption done');
let rawData = fs.readFileSync('encrypted.json')
let encryptedContent = JSON.parse(rawData);
//console.log(encryptedContent);
// B decrypting the file.
eccrypto.decrypt(privateKey, encryptedContent).then(function(plaintext) {
//console.log("Decrypted message: ", plaintext.toString());
fs.writeFile('decrypted.pdf', plaintext, function (err) {
if (err) return console.log(err);
console.log('decryption done');
});
});
});
I get following error from this code: "(node:271) UnhandledPromiseRejectionWarning: Error: Bad input."
Everything is working when i replace "encryptedContent" variable with "encrypted" on eccrypto.decrypt function but i want to let user store encrypted object and decrypt it later with this function. How can i do that?
The problem is the encrypted object is not exactly JSON serializable so you have to encode the buffers in some sort of JSON serializable object. Since you used hex for the private and public keys I used it as well at below. (Also the Buffer() constructor is depracated and not secure so I switched it to Buffer.from()
var fs = require('fs');
var crypto = require("crypto");
var eccrypto = require("eccrypto");
var content = fs.readFileSync('pdf_test.pdf');;
var importPrivateKey = "c337ded6f56c07205fb7b391654d7d463c9e0c726869523ae6024c9bec878878"
var importPublicKey = "04730a151f545f5dcdb1c6d99fb1251f5c70f216f39ba2681bcf10db16bd582e6720bc881d51f25ffbe961df6a0af24a9d39a4db3d86a7f6b3f9bf4eaac0e4006b"
let privateKey = Buffer.from(importPrivateKey, 'hex');
let publicKey = Buffer.from(importPublicKey, 'hex');
// Encrypting the file for B.
eccrypto.encrypt(publicKey, Buffer.from(content)).then(function (encrypted) {
//console.log('Encrypted message ' + JSON.stringify(encrypted));
let data = JSON.stringify({
iv: encrypted.iv.toString('hex'),
ciphertext: encrypted.ciphertext.toString('hex'),
mac: encrypted.mac.toString('hex'),
ephemPublicKey: encrypted.ephemPublicKey.toString('hex')
});
fs.writeFileSync('encrypted.json', data);
console.log('encryption done');
let rawData = fs.readFileSync('encrypted.json')
let encryptedContent = JSON.parse(rawData);
encryptedContent = {
iv: Buffer.from(encryptedContent.iv, 'hex'),
ciphertext: Buffer.from(encryptedContent.ciphertext, 'hex'),
mac: Buffer.from(encryptedContent.mac, 'hex'),
ephemPublicKey: Buffer.from(encryptedContent.ephemPublicKey, 'hex')
}
//console.log(encryptedContent);
// B decrypting the file.
eccrypto.decrypt(privateKey, encryptedContent).then(function (plaintext) {
//console.log("Decrypted message: ", plaintext.toString());
fs.writeFile('decrypted.pdf', plaintext, function (err) {
if (err) return console.log(err);
console.log('decryption done');
});
});
});

While doing decryption using crypto, getting bad decrypt error using Node.js

I am storing information in the database in an encrypted format. When I am retrieving it from the database and applying decrypt on it, I am getting error like "bad decrypt"
Here is my code:
const crypto = require("crypto");
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
function encrypt(text) {
let cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return { iv: iv.toString("hex"), encryptedData: encrypted.toString("hex") };
}
function decrypt(text) {
let iv = Buffer.from(text.iv, "hex");
let encryptedText = Buffer.from(text.encryptedData, "hex");
let decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
/********** following is the api **************/
exports.viewTrade = async (req, res) => {
console.log("viewTrade api ", req.query);
let maindData = [];
var hw = {
iv: "fc4ca9a17d97d7a7772449cfea3a99b8",
encryptedData: "e966509fd17785b4fe8304ef2f531806",
};
console.log(decrypt(hw));
const tradeList = await trade.find({
createdBy: req.query.id,
});
if (tradeList.length) {
for (tradeInfo of tradeList) {
// let nameInfo = tradeInfo.name;
// // let value = decrypt(nameInfo);
// console.log("name info.. ", nameInfo);
// // console.log("value.. ", value);
}
}
};
By calling the above API, it is throwing error.
When I try out your code I'm getting a error like this: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt.
This probably means that the key used to decrypt is not the same as the encryption key.
I suspect this is because the code above re-creates the key on each run.
I would suggest you use a fixed key, maybe coming from an environment variable, or secure secrets store. To test this you can just initialize your key like so:
const key = Buffer.from("232f150296ffd446fc0b39fa32d1c1d42c2e232ccd3203df729b7f3c3c63a5da2", "hex");
Then encrypt a message and see that it will decrypt correctly like so:
const encrypted = encrypt("We love Stack Overflow");
const decrypted = decrypt(encrypted);
console.log("encrypted data:", encrypted);
console.log("decrypted data:", decrypted);
You'll notice that if you copy the encrypted data from one run of the program to the next, it should work with a fixed key, but give you the bad_decrypt error if you generate the key on each run.
Key Generation code:
const key = crypto.randomBytes(32).toString("hex");

Encrypt and Decrypt files using Node.js Crypto Stream

I am making a Desktop application using Electron and react.js in which I have to encrypt and decrypt files (.txt, .pdf, .jpg, .png) using Crypto library.
I am using streams to do that.
So I just get the file from FileAPI and pass the file path to create readStream.
export function encrypt (passphrase) {
const crypto = require('crypto');
const fs = require('fs');
const cipher = crypto.createCipher('aes192', passphrase);
const input = fs.createReadStream(file_path);
const output = fs.createWriteStream('test.enc');
input.pipe(cipher).pipe(output);}
export function decrypt (passphrase) {
const crypto = require('crypto');
const fs = require('fs');
const cipher = crypto.createDecipher('aes192', passphrase);
const input = fs.createReadStream(file_path);
const output = fs.createWriteStream('test.pdf');}
input.pipe(cipher).pipe(output);
This code works fine for only for .txt files.
Any help to make this work for other file formats?
so here is a function that works.. just call on the function using 'data' in which ever format you need.. it seems best using strings or buffers.
function Encrypt_AES(data, pubkey) {
const algorithm = 'aes-192-cbc';
// Use the async `crypto.scrypt()` instead.
const key = crypto.scryptSync(pubkey, 'salt', 24);
// Use `crypto.randomBytes` to generate a random iv instead of the static iv
// shown here.
const iv = Buffer.alloc(16, 0); // Initialization vector.
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function Decrypt_AES(data, pubkey) {
const algorithm = 'aes-192-cbc';
// Use the async `crypto.scrypt()` instead.
const key = crypto.scryptSync(pubkey, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.
const decipher = crypto.createDecipheriv(algorithm, key, iv);
// Encrypted using same algorithm, key and iv.
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}

Resources