signal-desktop/app/key_management.js
Scott Nonnenberg 496ebf2a47 Store SQLCipher decryption key in separate file
First, we write the key a whole lot less. We write it on creation, then
never again.

Second, it's in a file we control very closely. Instead of blindly
regenerating the key if the target file generates an error on read,
we block startup unless the error is 'ENOENT' - the file isn't there
at all.

This still allows for the key.txt file to be deleted or corrupted
somehow, but it should be a lot less common than the high-traffic
config.json used for window location and media permissions.
2018-08-16 17:01:32 -07:00

62 lines
1.4 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { app } = require('electron');
const ENCODING = 'utf8';
const userDataPath = app.getPath('userData');
const targetPath = path.join(userDataPath, 'key.txt');
module.exports = {
get,
set,
initialize,
remove,
};
function get() {
try {
const key = fs.readFileSync(targetPath, ENCODING);
console.log('key/get: Successfully read key file');
return key;
} catch (error) {
if (error.code === 'ENOENT') {
console.log('key/get: Could not find key file, returning null');
return null;
}
throw error;
}
}
function set(key) {
console.log('key/set: Saving key to disk');
fs.writeFileSync(targetPath, key, ENCODING);
}
function remove() {
console.log('key/remove: Deleting key from disk');
fs.unlinkSync(targetPath);
}
function initialize({ userConfig }) {
const keyFromConfig = userConfig.get('key');
const keyFromStore = get();
let key = keyFromStore || keyFromConfig;
if (!key) {
console.log(
'key/initialize: Generating new encryption key, since we did not find it on disk'
);
// https://www.zetetic.net/sqlcipher/sqlcipher-api/#key
key = crypto.randomBytes(32).toString('hex');
set(key);
} else if (keyFromConfig) {
set(key);
console.log('key/initialize: Removing key from config.json');
userConfig.delete('key');
}
return key;
}