Add a header for encrypted backup files
this is required to identify old backups and new backups for decryption
This commit is contained in:
+20
-14
@@ -274,22 +274,25 @@ function decryptFilePath(filePath, encryption) {
|
||||
class EncryptStream extends TransformStream {
|
||||
constructor(encryption) {
|
||||
super();
|
||||
this._ivPushed = false;
|
||||
this._headerPushed = false;
|
||||
this._iv = crypto.randomBytes(16);
|
||||
this._cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryption.dataKey, 'hex'), this._iv);
|
||||
this._hmac = crypto.createHmac('sha256', Buffer.from(encryption.dataHmacKey, 'hex'));
|
||||
}
|
||||
|
||||
pushIvIfNeeded() {
|
||||
if (!this._ivPushed) {
|
||||
pushHeaderIfNeeded() {
|
||||
if (!this._headerPushed) {
|
||||
const magic = Buffer.from('CBV2');
|
||||
this.push(magic);
|
||||
this._hmac.update(magic);
|
||||
this.push(this._iv);
|
||||
this._hmac.update(this._iv);
|
||||
this._ivPushed = true;
|
||||
this._headerPushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
_transform(chunk, ignoredEncoding, callback) {
|
||||
this.pushIvIfNeeded();
|
||||
this.pushHeaderIfNeeded();
|
||||
|
||||
try {
|
||||
const crypt = this._cipher.update(chunk);
|
||||
@@ -302,7 +305,7 @@ class EncryptStream extends TransformStream {
|
||||
|
||||
_flush(callback) {
|
||||
try {
|
||||
this.pushIvIfNeeded(); // for 0-length files
|
||||
this.pushHeaderIfNeeded(); // for 0-length files
|
||||
const crypt = this._cipher.final();
|
||||
this.push(crypt);
|
||||
this._hmac.update(crypt);
|
||||
@@ -317,25 +320,28 @@ class DecryptStream extends TransformStream {
|
||||
constructor(encryption) {
|
||||
super();
|
||||
this._key = Buffer.from(encryption.dataKey, 'hex');
|
||||
this._iv = Buffer.alloc(0);
|
||||
this._header = 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;
|
||||
const needed = 20 - this._header.length; // 4 for magic, 16 for iv
|
||||
|
||||
if (this._iv.length !== 16) { // not gotten IV yet
|
||||
this._iv = Buffer.concat([this._iv, chunk.slice(0, needed)]);
|
||||
if (this._iv.length !== 16) return callback();
|
||||
if (this._header.length !== 20) { // not gotten header yet
|
||||
this._header = Buffer.concat([this._header, chunk.slice(0, needed)]);
|
||||
if (this._header.length !== 20) return callback();
|
||||
|
||||
this._decipher = crypto.createDecipheriv('aes-256-cbc', this._key, this._iv);
|
||||
this._hmac.update(this._iv);
|
||||
if (!this._header.slice(0, 4).equals(new Buffer.from('CBV2'))) return callback(new BoxError(BoxError.CRYPTO_ERROR, 'Invalid magic in header'));
|
||||
|
||||
const iv = this._header.slice(4);
|
||||
this._decipher = crypto.createDecipheriv('aes-256-cbc', this._key, iv);
|
||||
this._hmac.update(this._header);
|
||||
}
|
||||
|
||||
this._buffer = Buffer.concat([ this._buffer, chunk.slice(needed) ]);
|
||||
if (this._buffer.length < 32) return callback();
|
||||
if (this._buffer.length < 32) return callback(); // hmac trailer length is 32
|
||||
|
||||
try {
|
||||
const cipherText = this._buffer.slice(0, -32);
|
||||
|
||||
Reference in New Issue
Block a user