一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
253 行
11 KiB
TypeScript
253 行
11 KiB
TypeScript
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, encryptionService, expectNotThrow, expectThrow, kvStore, msleep } from '../../testing/test-utils';
|
|
import MasterKey from '../../models/MasterKey';
|
|
import { activeMasterKeySanityCheck, migrateMasterPassword, migratePpk, resetMasterPassword, showMissingMasterKeyMessage, showUnknownKeyFormatBanner, updateMasterPassword } from './utils';
|
|
import { localSyncInfo, masterKeyById, masterKeyEnabled, saveLocalSyncInfo, setActiveMasterKeyId, setMasterKeyEnabled, setPpk } from '../synchronizer/syncInfoUtils';
|
|
import Setting from '../../models/Setting';
|
|
import { generateKeyPair, generateKeyPairWithAlgorithm, getPpkAlgorithm, ppkPasswordIsValid, testing__setPpkMigrations_ } from './ppk/ppk';
|
|
import { PublicKeyAlgorithm } from './types';
|
|
import { EncryptionMethod } from './EncryptionService';
|
|
|
|
describe('e2ee/utils', () => {
|
|
|
|
beforeEach(async () => {
|
|
await setupDatabaseAndSynchronizer(1);
|
|
await switchClient(1);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await afterAllCleanUp();
|
|
});
|
|
|
|
it('should tell if the missing master key message should be shown', async () => {
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey('111111'));
|
|
const mk2 = await MasterKey.save(await encryptionService().generateMasterKey('111111'));
|
|
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id])).toBe(true);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id, mk2.id])).toBe(true);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [])).toBe(false);
|
|
|
|
setMasterKeyEnabled(mk1.id, false);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id])).toBe(false);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id, mk2.id])).toBe(true);
|
|
|
|
setMasterKeyEnabled(mk2.id, false);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id, mk2.id])).toBe(false);
|
|
|
|
setMasterKeyEnabled(mk1.id, true);
|
|
setMasterKeyEnabled(mk2.id, true);
|
|
expect(showMissingMasterKeyMessage(localSyncInfo(), [mk1.id, mk2.id])).toBe(true);
|
|
|
|
await expectNotThrow(async () => showMissingMasterKeyMessage(localSyncInfo(), ['not_downloaded_yet']));
|
|
|
|
const syncInfo = localSyncInfo();
|
|
syncInfo.masterKeys = [];
|
|
expect(showMissingMasterKeyMessage(syncInfo, [mk1.id, mk2.id])).toBe(false);
|
|
});
|
|
|
|
it.each([
|
|
[EncryptionMethod.KeyV1, false],
|
|
[1234, true],
|
|
[{ toString: 'throws if converted to string with String(...)' }, true],
|
|
])('should decide whether to show the unknown key format banner (encryption method: %j, should show: %j)', async (encryptionMethod, shouldShow) => {
|
|
await MasterKey.save({
|
|
...await encryptionService().generateMasterKey('111111'),
|
|
encryption_method: encryptionMethod as unknown as number,
|
|
});
|
|
|
|
expect(showUnknownKeyFormatBanner(localSyncInfo())).toBe(shouldShow);
|
|
});
|
|
|
|
it('should do ppk migration', async () => {
|
|
const { reset } = testing__setPpkMigrations_([PublicKeyAlgorithm.RsaV1, PublicKeyAlgorithm.RsaV2]);
|
|
|
|
try {
|
|
const syncInfo = localSyncInfo();
|
|
const testPassword = 'test--TEST';
|
|
Setting.setValue('encryption.masterPassword', testPassword);
|
|
syncInfo.ppk = await generateKeyPairWithAlgorithm(PublicKeyAlgorithm.RsaV1, encryptionService(), testPassword);
|
|
saveLocalSyncInfo(syncInfo);
|
|
|
|
expect(getPpkAlgorithm(localSyncInfo().ppk)).toBe(PublicKeyAlgorithm.RsaV1);
|
|
await migratePpk();
|
|
expect(getPpkAlgorithm(localSyncInfo().ppk)).toBe(PublicKeyAlgorithm.RsaV2);
|
|
} finally {
|
|
reset();
|
|
}
|
|
});
|
|
|
|
it('should not undo ppk migration when the most recent public key algorithm is not in the migrations list', async () => {
|
|
const { reset } = testing__setPpkMigrations_([PublicKeyAlgorithm.RsaV1]);
|
|
|
|
try {
|
|
const syncInfo = localSyncInfo();
|
|
const testPassword = 'test--TEST';
|
|
Setting.setValue('encryption.masterPassword', testPassword);
|
|
syncInfo.ppk = await generateKeyPairWithAlgorithm(PublicKeyAlgorithm.RsaV2, encryptionService(), testPassword);
|
|
saveLocalSyncInfo(syncInfo);
|
|
|
|
expect(getPpkAlgorithm(localSyncInfo().ppk)).toBe(PublicKeyAlgorithm.RsaV2);
|
|
await migratePpk();
|
|
expect(getPpkAlgorithm(localSyncInfo().ppk)).toBe(PublicKeyAlgorithm.RsaV2);
|
|
} finally {
|
|
reset();
|
|
}
|
|
});
|
|
|
|
it('should not migrate ppk if the key is up-to-date', async () => {
|
|
const syncInfo = localSyncInfo();
|
|
const testPassword = 'test 🔑';
|
|
Setting.setValue('encryption.masterPassword', testPassword);
|
|
const originalPpk = await generateKeyPair(encryptionService(), testPassword);
|
|
syncInfo.ppk = originalPpk;
|
|
saveLocalSyncInfo(syncInfo);
|
|
|
|
const lastChangeTime = localSyncInfo().keyTimestamp('ppk');
|
|
await migratePpk();
|
|
expect(localSyncInfo().keyTimestamp('ppk')).toBe(lastChangeTime);
|
|
expect(localSyncInfo().ppk.createdTime).toBe(originalPpk.createdTime);
|
|
});
|
|
|
|
it('should do the master password migration', async () => {
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey('111111'));
|
|
const mk2 = await MasterKey.save(await encryptionService().generateMasterKey('222222'));
|
|
|
|
Setting.setValue('encryption.passwordCache', {
|
|
[mk1.id]: '111111',
|
|
[mk2.id]: '222222',
|
|
});
|
|
|
|
await migrateMasterPassword();
|
|
|
|
{
|
|
expect(Setting.value('encryption.masterPassword')).toBe('');
|
|
const newCache = Setting.value('encryption.passwordCache');
|
|
expect(newCache[mk1.id]).toBe('111111');
|
|
expect(newCache[mk2.id]).toBe('222222');
|
|
}
|
|
|
|
setActiveMasterKeyId(mk1.id);
|
|
await migrateMasterPassword();
|
|
|
|
{
|
|
expect(Setting.value('encryption.masterPassword')).toBe('111111');
|
|
const newCache = Setting.value('encryption.passwordCache');
|
|
expect(newCache[mk1.id]).toBe(undefined);
|
|
expect(newCache[mk2.id]).toBe('222222');
|
|
}
|
|
});
|
|
|
|
it('should update the master password', async () => {
|
|
const masterPassword1 = '111111';
|
|
const masterPassword2 = '222222';
|
|
Setting.setValue('encryption.masterPassword', masterPassword1);
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
const mk2 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
|
|
setPpk(await generateKeyPair(encryptionService(), masterPassword1));
|
|
|
|
await updateMasterPassword(masterPassword1, masterPassword2);
|
|
|
|
expect(Setting.value('encryption.masterPassword')).toBe(masterPassword2);
|
|
expect(await ppkPasswordIsValid(encryptionService(), localSyncInfo().ppk, masterPassword1)).toBe(false);
|
|
expect(await ppkPasswordIsValid(encryptionService(), localSyncInfo().ppk, masterPassword2)).toBe(true);
|
|
expect(await encryptionService().checkMasterKeyPassword(await MasterKey.load(mk1.id), masterPassword1)).toBe(false);
|
|
expect(await encryptionService().checkMasterKeyPassword(await MasterKey.load(mk2.id), masterPassword1)).toBe(false);
|
|
expect(await encryptionService().checkMasterKeyPassword(await MasterKey.load(mk1.id), masterPassword2)).toBe(true);
|
|
expect(await encryptionService().checkMasterKeyPassword(await MasterKey.load(mk2.id), masterPassword2)).toBe(true);
|
|
|
|
await expectThrow(async () => updateMasterPassword('wrong', masterPassword1));
|
|
});
|
|
|
|
it('should set and verify master password when a data key exists', async () => {
|
|
const password = '111111';
|
|
|
|
await MasterKey.save(await encryptionService().generateMasterKey(password));
|
|
|
|
await expectThrow(async () => updateMasterPassword('', 'wrong'));
|
|
await expectNotThrow(async () => updateMasterPassword('', password));
|
|
expect(Setting.value('encryption.masterPassword')).toBe(password);
|
|
});
|
|
|
|
it('should set and verify master password when a private key exists', async () => {
|
|
const password = '111111';
|
|
|
|
setPpk(await generateKeyPair(encryptionService(), password));
|
|
|
|
await expectThrow(async () => updateMasterPassword('', 'wrong'));
|
|
await expectNotThrow(async () => updateMasterPassword('', password));
|
|
expect(Setting.value('encryption.masterPassword')).toBe(password);
|
|
});
|
|
|
|
it('should only set the master password if not already set', async () => {
|
|
expect(localSyncInfo().ppk).toBeFalsy();
|
|
await updateMasterPassword('', '111111');
|
|
expect(Setting.value('encryption.masterPassword')).toBe('111111');
|
|
expect(localSyncInfo().ppk).toBeFalsy();
|
|
expect(localSyncInfo().masterKeys.length).toBe(0);
|
|
});
|
|
|
|
it('should change the master password even if no key is present', async () => {
|
|
await updateMasterPassword('', '111111');
|
|
expect(Setting.value('encryption.masterPassword')).toBe('111111');
|
|
|
|
await updateMasterPassword('111111', '222222');
|
|
expect(Setting.value('encryption.masterPassword')).toBe('222222');
|
|
});
|
|
|
|
it('should reset a master password', async () => {
|
|
const masterPassword1 = '111111';
|
|
const masterPassword2 = '222222';
|
|
Setting.setValue('encryption.masterPassword', masterPassword1);
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
const mk2 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
setPpk(await generateKeyPair(encryptionService(), masterPassword1));
|
|
|
|
const previousPpk = localSyncInfo().ppk;
|
|
await resetMasterPassword(encryptionService(), kvStore(), null, masterPassword2);
|
|
|
|
expect(masterKeyEnabled(masterKeyById(mk1.id))).toBe(false);
|
|
expect(masterKeyEnabled(masterKeyById(mk2.id))).toBe(false);
|
|
expect(localSyncInfo().ppk.id).not.toBe(previousPpk.id);
|
|
expect(localSyncInfo().ppk.privateKey.ciphertext).not.toBe(previousPpk.privateKey.ciphertext);
|
|
expect(localSyncInfo().ppk.publicKey).not.toBe(previousPpk.publicKey);
|
|
|
|
// Also check that a new master key has been created, that it is active and enabled
|
|
expect(localSyncInfo().masterKeys.length).toBe(3);
|
|
expect(localSyncInfo().activeMasterKeyId).toBe(localSyncInfo().masterKeys[2].id);
|
|
expect(masterKeyEnabled(localSyncInfo().masterKeys[2])).toBe(true);
|
|
});
|
|
|
|
it('should fix active key selection issues - 1', async () => {
|
|
const masterPassword1 = '111111';
|
|
Setting.setValue('encryption.masterPassword', masterPassword1);
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
await msleep(1);
|
|
await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
await msleep(1);
|
|
const mk3 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
setActiveMasterKeyId(mk1.id);
|
|
setMasterKeyEnabled(mk1.id, false);
|
|
|
|
activeMasterKeySanityCheck();
|
|
|
|
const syncInfo = localSyncInfo();
|
|
expect(syncInfo.activeMasterKeyId).toBe(mk3.id);
|
|
});
|
|
|
|
it('should fix active key selection issues - 2', async () => {
|
|
// Should not do anything if the active key is already an enabled one.
|
|
const masterPassword1 = '111111';
|
|
Setting.setValue('encryption.masterPassword', masterPassword1);
|
|
const mk1 = await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
await msleep(1);
|
|
await MasterKey.save(await encryptionService().generateMasterKey(masterPassword1));
|
|
setActiveMasterKeyId(mk1.id);
|
|
|
|
activeMasterKeySanityCheck();
|
|
|
|
const syncInfo = localSyncInfo();
|
|
expect(syncInfo.activeMasterKeyId).toBe(mk1.id);
|
|
});
|
|
|
|
});
|