mirror of https://github.com/keeweb/keeweb.git
93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
const { ipcMain } = require('electron');
|
|
const { readXoredValue, makeXoredValue } = require('../util/byte-utils');
|
|
const { reqNative } = require('../util/req-native');
|
|
const { isDev } = require('../util/app-info');
|
|
|
|
ipcMain.handle('hardwareCryptoDeleteKey', hardwareCryptoDeleteKey);
|
|
ipcMain.handle('hardwareEncrypt', hardwareEncrypt);
|
|
ipcMain.handle('hardwareDecrypt', hardwareDecrypt);
|
|
|
|
const keyTag = 'net.antelle.keeweb.encryption-key';
|
|
|
|
let testCipherParams;
|
|
let keyChecked = false;
|
|
|
|
async function hardwareCryptoDeleteKey() {
|
|
const secureEnclave = reqNative('secure-enclave');
|
|
await secureEnclave.deleteKeyPair({ keyTag });
|
|
keyChecked = false;
|
|
}
|
|
|
|
async function hardwareEncrypt(e, value) {
|
|
return await hardwareCrypto(value, true);
|
|
}
|
|
|
|
async function hardwareDecrypt(e, value, touchIdPrompt) {
|
|
return await hardwareCrypto(value, false, touchIdPrompt);
|
|
}
|
|
|
|
async function hardwareCrypto(value, encrypt, touchIdPrompt) {
|
|
if (process.platform !== 'darwin') {
|
|
throw new Error('Not supported');
|
|
}
|
|
|
|
// This is a native module, but why is it here and not in native-module-host?
|
|
// It's because native-module-host is started as a fork,
|
|
// and macOS thinks it doesn't have necessary entitlements,
|
|
// so any attempt to use Secure Enclave API fails with an error.
|
|
|
|
const secureEnclave = reqNative('secure-enclave');
|
|
|
|
const data = readXoredValue(value);
|
|
|
|
let res;
|
|
if (isDev && process.env.KEEWEB_EMULATE_HARDWARE_ENCRYPTION) {
|
|
const crypto = require('crypto');
|
|
if (!testCipherParams) {
|
|
let key, iv;
|
|
if (process.env.KEEWEB_EMULATE_HARDWARE_ENCRYPTION === 'persistent') {
|
|
key = Buffer.alloc(32, 0);
|
|
iv = Buffer.alloc(16, 0);
|
|
} else {
|
|
key = crypto.randomBytes(32);
|
|
iv = crypto.randomBytes(16);
|
|
}
|
|
testCipherParams = { key, iv };
|
|
}
|
|
const { key, iv } = testCipherParams;
|
|
const algo = 'aes-256-cbc';
|
|
let cipher;
|
|
if (encrypt) {
|
|
cipher = crypto.createCipheriv(algo, key, iv);
|
|
} else {
|
|
cipher = crypto.createDecipheriv(algo, key, iv);
|
|
}
|
|
res = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
} else {
|
|
if (encrypt) {
|
|
await checkKey();
|
|
res = await secureEnclave.encrypt({ keyTag, data });
|
|
} else {
|
|
res = await secureEnclave.decrypt({ keyTag, data, touchIdPrompt });
|
|
}
|
|
}
|
|
|
|
data.fill(0);
|
|
|
|
return makeXoredValue(res);
|
|
|
|
async function checkKey() {
|
|
if (keyChecked) {
|
|
return;
|
|
}
|
|
try {
|
|
await secureEnclave.createKeyPair({ keyTag });
|
|
keyChecked = true;
|
|
} catch (e) {
|
|
if (!e.keyExists) {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|