Girish Ramakrishnan
2020-05-12 16:17:57 -07:00
parent ea8a3d798e
commit 37dfa41e01
+30 -12
View File
@@ -260,20 +260,23 @@ function decryptFilePath(filePath, backupConfig) {
}
class EncryptStream extends TransformStream {
constructor(key) {
constructor(encryption) {
super();
this._ivPushed = false;
this._iv = crypto.randomBytes(16);
this._cipher = crypto.createCipheriv('aes-256-cbc', key, this._iv);
this._cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryption.dataKey, 'hex'), this._iv);
this._hmac = crypto.createHmac('sha256', Buffer.from(encryption.dataHmacKey, 'hex'));
}
_transform(chunk, ignoredEncoding, callback) {
if (!this._ivPushed) {
this.push(this._iv);
this._hmac.update(this._iv);
this._ivPushed = true;
}
try {
const crypt = this._cipher.update(chunk);
this._hmac.update(crypt);
callback(null, crypt);
} catch (error) {
callback(error);
@@ -283,7 +286,9 @@ class EncryptStream extends TransformStream {
_flush(callback) {
try {
const crypt = this._cipher.final();
callback(null, crypt);
this.push(crypt);
this._hmac.update(crypt);
callback(null, this._hmac.digest());
} catch (error) {
callback(error);
}
@@ -291,25 +296,34 @@ class EncryptStream extends TransformStream {
}
class DecryptStream extends TransformStream {
constructor(key) {
constructor(encryption) {
super();
this._key = key;
this._key = Buffer.from(encryption.dataKey, 'hex');
this._iv = Buffer.alloc(0);
this._decipher = null;
this._hmac = crypto.createHmac('sha256', Buffer.from(encryption.dataHmacKey, 'hex'));
this._buffer = Buffer.alloc(0);
}
_transform(chunk, ignoredEncoding, callback) {
const needed = 16 - this._iv.length;
if (this._iv.length !== 16) { // not gotten IV yet
const needed = 16 - this._iv.length;
this._iv = Buffer.concat([this._iv, chunk.slice(0, needed)]);
if (this._iv.length !== 16) return callback();
this._decipher = crypto.createDecipheriv('aes-256-cbc', this._key, this._iv);
chunk = chunk.slice(needed);
this._hmac.update(this._iv);
}
this._buffer = Buffer.concat([ this._buffer, chunk.slice(needed) ]);
if (this._buffer.length < 32) return callback();
try {
const plainText = this._decipher.update(chunk);
const cipherText = this._buffer.slice(0, -32);
this._hmac.update(cipherText);
const plainText = this._decipher.update(cipherText);
this._buffer = this._buffer.slice(-32);
callback(null, plainText);
} catch (error) {
callback(error);
@@ -317,7 +331,11 @@ class DecryptStream extends TransformStream {
}
_flush (callback) {
if (this._buffer.length !== 32) return callback(new BoxError(BoxError.CRYPTO_ERROR, 'Invalid password or tampered file (not enough data)'));
try {
if (!this._hmac.digest().equals(this._buffer)) return callback(new BoxError(BoxError.CRYPTO_ERROR, 'Invalid password or tampered file (mac mismatch)'));
const plainText = this._decipher.final();
callback(null, plainText);
} catch (error) {
@@ -339,7 +357,7 @@ function createReadStream(sourceFile, backupConfig) {
});
if (backupConfig.encryption) {
let encryptStream = new EncryptStream(Buffer.from(backupConfig.encryption.dataKey, 'hex'));
let encryptStream = new EncryptStream(backupConfig.encryption);
encryptStream.on('error', function (error) {
debug('createReadStream: encrypt stream error.', error);
@@ -365,7 +383,7 @@ function createWriteStream(destFile, backupConfig) {
});
if (backupConfig.encryption) {
let decrypt = new DecryptStream(Buffer.from(backupConfig.encryption.dataKey, 'hex'));
let decrypt = new DecryptStream(backupConfig.encryption);
decrypt.on('error', function (error) {
debug('createWriteStream: decrypt stream error.', error);
ps.emit('error', new BoxError(BoxError.CRYPTO_ERROR, error.message));
@@ -415,7 +433,7 @@ function tarPack(dataLayout, backupConfig, callback) {
});
if (backupConfig.encryption) {
const encryptStream = new EncryptStream(Buffer.from(backupConfig.encryption.dataKey, 'hex'));
const encryptStream = new EncryptStream(backupConfig.encryption);
encryptStream.on('error', function (error) {
debug('tarPack: encrypt stream error.', error);
ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message));
@@ -626,7 +644,7 @@ function tarExtract(inStream, dataLayout, backupConfig, callback) {
});
if (backupConfig.encryption) {
let decrypt = new DecryptStream(Buffer.from(backupConfig.encryption.dataKey, 'hex'));
let decrypt = new DecryptStream(backupConfig.encryption);
decrypt.on('error', function (error) {
debug('tarExtract: decrypt stream error.', error);
emitError(new BoxError(BoxError.EXTERNAL_ERROR, `Failed to decrypt: ${error.message}`));