You've already forked joplin
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:
@ -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!');
|
||||
|
Reference in New Issue
Block a user