1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-06-27 23:28:38 +02:00

All: Add support for public-private key pairs and improved master password support (#5438)

Also improved SCSS support, which was needed for the master password dialog.
This commit is contained in:
Laurent
2021-10-03 16:00:49 +01:00
committed by GitHub
parent e5e1382255
commit c758377188
72 changed files with 4495 additions and 5296 deletions

View File

@ -3,8 +3,8 @@ import { _ } from '../../locale';
import BaseItem, { EncryptedItemsStats } from '../../models/BaseItem';
import useAsyncEffect, { AsyncEffectEvent } from '../../hooks/useAsyncEffect';
import { MasterKeyEntity } from '../../services/e2ee/types';
import time from '../../time';
import { findMasterKeyPassword } from '../../services/e2ee/utils';
// import time from '../../time';
import { findMasterKeyPassword, getMasterPasswordStatus, masterPasswordIsValid, MasterPasswordStatus } from '../../services/e2ee/utils';
import EncryptionService from '../../services/e2ee/EncryptionService';
import { masterKeyEnabled, setMasterKeyEnabled } from '../../services/synchronizer/syncInfoUtils';
import MasterKey from '../../models/MasterKey';
@ -21,7 +21,10 @@ export const useStats = () => {
useAsyncEffect(async (event: AsyncEffectEvent) => {
const r = await BaseItem.encryptedItemsStats();
if (event.cancelled) return;
setStats(r);
setStats(stats => {
if (JSON.stringify(stats) === JSON.stringify(r)) return stats;
return r;
});
}, [statsUpdateTime]);
useEffect(() => {
@ -30,7 +33,7 @@ export const useStats = () => {
}, 3000);
return () => {
clearInterval(iid);
shim.clearInterval(iid);
};
}, []);
@ -44,20 +47,18 @@ export const decryptedStatText = (stats: EncryptedItemsStats) => {
return result;
};
export const enableEncryptionConfirmationMessages = (masterKey: MasterKeyEntity) => {
const msg = [_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.')];
if (masterKey) msg.push(_('Encryption will be enabled using the master key created on %s', time.unixMsToLocalDateTime(masterKey.created_time)));
return msg;
};
export const enableEncryptionConfirmationMessages = (_masterKey: MasterKeyEntity, hasMasterPassword: boolean) => {
const msg = [_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target.')];
const masterPasswordIsValid = async (masterKeys: MasterKeyEntity[], activeMasterKeyId: string, masterPassword: string = null) => {
const activeMasterKey = masterKeys.find((mk: MasterKeyEntity) => mk.id === activeMasterKeyId);
masterPassword = masterPassword === null ? masterPassword : masterPassword;
if (activeMasterKey && masterPassword) {
return EncryptionService.instance().checkMasterKeyPassword(activeMasterKey, masterPassword);
if (hasMasterPassword) {
msg.push(_('To continue, please enter your master password below.'));
} else {
msg.push(_('Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.'));
}
return false;
// if (masterKey) msg.push(_('Encryption will be enabled using the master key created on %s', time.unixMsToLocalDateTime(masterKey.created_time)));
return msg;
};
export const reencryptData = async () => {
@ -107,7 +108,7 @@ export const useInputMasterPassword = (masterKeys: MasterKeyEntity[], activeMast
const onMasterPasswordSave = useCallback(async () => {
Setting.setValue('encryption.masterPassword', inputMasterPassword);
if (!(await masterPasswordIsValid(masterKeys, activeMasterKeyId, inputMasterPassword))) {
if (!(await masterPasswordIsValid(inputMasterPassword, masterKeys.find(mk => mk.id === activeMasterKeyId)))) {
alert('Password is invalid. Please try again.');
}
}, [inputMasterPassword]);
@ -141,6 +142,7 @@ export const useInputPasswords = (propsPasswords: Record<string, string>) => {
export const usePasswordChecker = (masterKeys: MasterKeyEntity[], activeMasterKeyId: string, masterPassword: string, passwords: Record<string, string>) => {
const [passwordChecks, setPasswordChecks] = useState<PasswordChecks>({});
const [masterPasswordKeys, setMasterPasswordKeys] = useState<PasswordChecks>({});
const [masterPasswordStatus, setMasterPasswordStatus] = useState<MasterPasswordStatus>(MasterPasswordStatus.Unknown);
useAsyncEffect(async (event: AsyncEffectEvent) => {
const newPasswordChecks: PasswordChecks = {};
@ -154,15 +156,25 @@ export const usePasswordChecker = (masterKeys: MasterKeyEntity[], activeMasterKe
newMasterPasswordKeys[mk.id] = password === masterPassword;
}
newPasswordChecks['master'] = await masterPasswordIsValid(masterKeys, activeMasterKeyId, masterPassword);
newPasswordChecks['master'] = masterPassword ? await masterPasswordIsValid(masterPassword, masterKeys.find(mk => mk.id === activeMasterKeyId)) : true;
if (event.cancelled) return;
setPasswordChecks(newPasswordChecks);
setMasterPasswordKeys(newMasterPasswordKeys);
setPasswordChecks(passwordChecks => {
if (JSON.stringify(newPasswordChecks) === JSON.stringify(passwordChecks)) return passwordChecks;
return newPasswordChecks;
});
setMasterPasswordKeys(masterPasswordKeys => {
if (JSON.stringify(newMasterPasswordKeys) === JSON.stringify(masterPasswordKeys)) return masterPasswordKeys;
console.info('====', JSON.stringify(newMasterPasswordKeys), JSON.stringify(masterPasswordKeys));
return newMasterPasswordKeys;
});
setMasterPasswordStatus(await getMasterPasswordStatus(masterPassword));
}, [masterKeys, masterPassword]);
return { passwordChecks, masterPasswordKeys };
return { passwordChecks, masterPasswordKeys, masterPasswordStatus };
};
export const upgradeMasterKey = async (masterKey: MasterKeyEntity, passwordChecks: PasswordChecks, passwords: Record<string, string>): Promise<string> => {
@ -173,7 +185,7 @@ export const upgradeMasterKey = async (masterKey: MasterKeyEntity, passwordCheck
try {
const password = passwords[masterKey.id];
const newMasterKey = await EncryptionService.instance().upgradeMasterKey(masterKey, password);
const newMasterKey = await EncryptionService.instance().reencryptMasterKey(masterKey, password, password);
await MasterKey.save(newMasterKey);
void reg.waitForSyncFinishedThenSync();
return _('The master key has been upgraded successfully!');