Skip to content

Commit

Permalink
Changed database file encryption to pbkdf2 for key derivation and aes…
Browse files Browse the repository at this point in the history
…-256-gcm for encryption (#6296)

* Added pbkdf2 and aes-256-gcm options for database file encryption

* Added dbCipherAlgorithm option

* Changed pbkdf2 to default

Maintains backward compatibility, but will require a manual repush to update to the new version

* Removed dbkeyderivationiterations option, as this branch is to be more opinionated
  • Loading branch information
HuFlungDu authored Aug 4, 2024
1 parent 41e4213 commit 5b76a31
Showing 1 changed file with 48 additions and 10 deletions.
58 changes: 48 additions & 10 deletions db.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,34 +417,72 @@ module.exports.CreateDB = function (parent, func) {
};

// Get encryption key
obj.getEncryptDataKey = function (password) {
obj.getEncryptDataKey = function (password, salt, iterations) {
if (typeof password != 'string') return null;
let key;
try {
key = parent.crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha384');
} catch (e) {
// If this previous call fails, it's probably because older pbkdf2 did not specify the hashing function, just use the default.
key = parent.crypto.pbkdf2Sync(password, salt, iterations, 32);
}
return key
}

obj.oldGetEncryptDataKey = function (password) {
if (typeof password != 'string') return null;
return parent.crypto.createHash('sha384').update(password).digest("raw").slice(0, 32);
}

// Encrypt data
obj.encryptData = function (password, plaintext) {
var key = obj.getEncryptDataKey(password);
if (key == null) return null;
let encryptionVersion = 0x1;
let iterations = 100000
const iv = parent.crypto.randomBytes(16);
const aes = parent.crypto.createCipheriv('aes-256-cbc', key, iv);
var key = obj.getEncryptDataKey(password, iv, iterations);
if (key == null) return null;
const aes = parent.crypto.createCipheriv("aes-256-gcm", key, iv);
var ciphertext = aes.update(plaintext);
ciphertext = Buffer.concat([iv, ciphertext, aes.final()]);
let versionbuf = Buffer.allocUnsafe(2);
versionbuf.writeUInt16BE(encryptionVersion);
let iterbuf = Buffer.allocUnsafe(4);
iterbuf.writeUInt32BE(iterations);
let encryptedBuf = aes.final();
ciphertext = Buffer.concat([versionbuf, iterbuf, aes.getAuthTag(), iv, ciphertext, encryptedBuf]);
return ciphertext.toString('base64');
}

// Decrypt data
obj.decryptData = function (password, ciphertext) {
let ciphertextBytes = Buffer.from(ciphertext, 'base64');
try {
var key = obj.getEncryptDataKey(password);
if (key == null) return null;
const ciphertextBytes = Buffer.from(ciphertext, 'base64');
const iv = ciphertextBytes.slice(0, 16);
const data = ciphertextBytes.slice(16);
const aes = parent.crypto.createDecipheriv('aes-256-cbc', key, iv);
var plaintextBytes = Buffer.from(aes.update(data));
let key = obj.oldGetEncryptDataKey(password);
const aes = parent.crypto.createDecipheriv("aes-256-cbc", key, iv);
let plaintextBytes = Buffer.from(aes.update(data));
plaintextBytes = Buffer.concat([plaintextBytes, aes.final()]);
return plaintextBytes;
} catch (e) {}
// Adding an encryption version lets us avoid try catching in the future
let encryptionVersion = ciphertextBytes.readUInt16BE(0);
try {
switch (encryptionVersion) {
case 0x1:
let iterations = ciphertextBytes.readUInt32BE(2);
let authTag = ciphertextBytes.slice(6, 22);
const iv = ciphertextBytes.slice(22, 38);
const data = ciphertextBytes.slice(38);
let key = obj.getEncryptDataKey(password, iv, iterations);
if (key == null) return null;
const aes = parent.crypto.createDecipheriv("aes-256-gcm", key, iv);
aes.setAuthTag(authTag);
let plaintextBytes = Buffer.from(aes.update(data));
plaintextBytes = Buffer.concat([plaintextBytes, aes.final()]);
return plaintextBytes;
default:
return null;
}
} catch (ex) { return null; }
}

Expand Down

0 comments on commit 5b76a31

Please sign in to comment.