1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/lib/services/e2ee/utils.ts
2021-08-17 12:03:19 +01:00

106 lines
4.0 KiB
TypeScript

import Logger from '../../Logger';
import BaseItem from '../../models/BaseItem';
import MasterKey from '../../models/MasterKey';
import Setting from '../../models/Setting';
import { MasterKeyEntity } from './types';
import EncryptionService from '../EncryptionService';
import { getActiveMasterKeyId, masterKeyEnabled, setEncryptionEnabled, SyncInfo } from '../synchronizer/syncInfoUtils';
const logger = Logger.create('e2ee/utils');
export async function setupAndEnableEncryption(service: EncryptionService, masterKey: MasterKeyEntity = null, password: string = null) {
if (!masterKey) {
// May happen for example if there are master keys in info.json but none
// of them is set as active. But in fact, unless there is a bug in the
// application, this shouldn't happen.
logger.warn('Setting up E2EE without a master key - user will need to either generate one or select one of the existing ones as active');
}
setEncryptionEnabled(true, masterKey ? masterKey.id : null);
if (masterKey && password) {
const passwordCache = Setting.value('encryption.passwordCache');
passwordCache[masterKey.id] = password;
Setting.setValue('encryption.passwordCache', passwordCache);
}
// Mark only the non-encrypted ones for sync since, if there are encrypted ones,
// it means they come from the sync target and are already encrypted over there.
await BaseItem.markAllNonEncryptedForSync();
await loadMasterKeysFromSettings(service);
}
export async function setupAndDisableEncryption(service: EncryptionService) {
// Allow disabling encryption even if some items are still encrypted, because whether E2EE is enabled or disabled
// should not affect whether items will enventually be decrypted or not (DecryptionWorker will still work as
// long as there are encrypted items). Also even if decryption is disabled, it's possible that encrypted items
// will still be received via synchronisation.
setEncryptionEnabled(false);
// The only way to make sure everything gets decrypted on the sync target is
// to re-sync everything.
await BaseItem.forceSyncAll();
await loadMasterKeysFromSettings(service);
}
export async function toggleAndSetupEncryption(service: EncryptionService, enabled: boolean, masterKey: MasterKeyEntity, password: string) {
if (!enabled) {
await setupAndDisableEncryption(service);
} else {
if (masterKey) {
await setupAndEnableEncryption(service, masterKey, password);
} else {
await generateMasterKeyAndEnableEncryption(EncryptionService.instance(), password);
}
}
await loadMasterKeysFromSettings(service);
}
export async function generateMasterKeyAndEnableEncryption(service: EncryptionService, password: string) {
let masterKey = await service.generateMasterKey(password);
masterKey = await MasterKey.save(masterKey);
await setupAndEnableEncryption(service, masterKey, password);
await loadMasterKeysFromSettings(service);
return masterKey;
}
export async function loadMasterKeysFromSettings(service: EncryptionService) {
const masterKeys = await MasterKey.all();
const passwords = Setting.value('encryption.passwordCache');
const activeMasterKeyId = getActiveMasterKeyId();
logger.info(`Trying to load ${masterKeys.length} master keys...`);
for (let i = 0; i < masterKeys.length; i++) {
const mk = masterKeys[i];
const password = passwords[mk.id];
if (service.isMasterKeyLoaded(mk)) continue;
if (!password) continue;
try {
await service.loadMasterKey(mk, password, activeMasterKeyId === mk.id);
} catch (error) {
logger.warn(`Cannot load master key ${mk.id}. Invalid password?`, error);
}
}
logger.info(`Loaded master keys: ${service.loadedMasterKeysCount()}`);
}
export function showMissingMasterKeyMessage(syncInfo: SyncInfo, notLoadedMasterKeys: string[]) {
if (!syncInfo.masterKeys.length) return false;
notLoadedMasterKeys = notLoadedMasterKeys.slice();
for (let i = notLoadedMasterKeys.length - 1; i >= 0; i--) {
const mk = syncInfo.masterKeys.find(mk => mk.id === notLoadedMasterKeys[i]);
if (!masterKeyEnabled(mk)) notLoadedMasterKeys.pop();
}
return !!notLoadedMasterKeys.length;
}