文件
mynode/packages/lib/services/noteLock/NoteLockKey.ts
chen 301194963b
一些检查失败了
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
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

101 行
3.5 KiB
TypeScript

import uuid from '../../uuid';
import Setting from '../../models/Setting';
import EncryptionService from '../e2ee/EncryptionService';
import { MasterKeyEntity } from '../e2ee/types';
import { localSyncInfo, saveLocalSyncInfo } from '../synchronizer/syncInfoUtils';
export interface DecryptedNoteLockKey {
id: string;
plainText: string;
}
// Importers that predate note lock skip this file, since they only read .md files.
export const noteLockKeyFileName = 'note_lock_key.json';
export default class NoteLockKey {
public static instance_: NoteLockKey = null;
private constructor(private encryptionService_: EncryptionService = EncryptionService.instance()) {}
public static instance() {
if (!this.instance_) {
this.instance_ = new NoteLockKey();
}
return this.instance_;
}
public static destroyInstance() {
this.instance_ = null;
}
public load(): MasterKeyEntity {
return localSyncInfo().noteLockKey;
}
public save(o: MasterKeyEntity): MasterKeyEntity {
const key = { ...o };
if (!key.id) {
key.id = uuid.create();
key.created_time = Date.now();
}
key.updated_time = Date.now();
const syncInfo = localSyncInfo();
syncInfo.noteLockKey = key;
// Only a key migration replaces the sync migration id, so a reset keeps the lineage it belongs to.
if (!syncInfo.syncMigrationId) syncInfo.syncMigrationId = uuid.create();
saveLocalSyncInfo(syncInfo);
return key;
}
public async create(password: string) {
if (this.load()) throw new Error('Note lock key already exists');
const key = await this.encryptionService_.generateMasterKey(password);
// A key may have arrived through sync while the new one was being generated - keep it,
// since notes may already be locked with it on another device.
if (this.load()) throw new Error('Note lock key already exists');
return this.save(key);
}
// Rotate through NoteLockSession.reset() rather than calling this directly, so the session locks
// and drops the old key as part of the rotation.
public async reset(password: string) {
const generated = await this.encryptionService_.generateMasterKey(password);
// Read once the key is ready, so a sync that finished meanwhile counts; a second reset before that sync keeps the first backup.
if (!Setting.value('noteLock.keyIdToReset')) Setting.setValue('noteLock.keyIdToReset', this.load()?.id ?? '');
const key = this.save(generated);
// Lets the next sync push the new key to the target instead of adopting the old one back.
Setting.setValue('noteLock.passwordReset', true);
return key;
}
public async changePassword(currentPassword: string, newPassword: string) {
const key = this.load();
if (!key) throw new Error('Note lock key has not been created');
return this.save(await this.encryptionService_.reencryptMasterKey(key, currentPassword, newPassword));
}
public needsUpgrade() {
const key = this.load();
return !!key && !!this.encryptionService_.masterKeysThatNeedUpgrading([key]).length;
}
public async upgrade(password: string) {
const key = this.load();
if (!key) throw new Error('Note lock key has not been created');
if (!this.needsUpgrade()) return key;
return this.changePassword(password, password);
}
public async decrypt(password: string, key: MasterKeyEntity = null): Promise<DecryptedNoteLockKey> {
if (!key) key = this.load();
if (!key) throw new Error('Note lock key has not been created');
if (!key.id) throw new Error('Note lock key does not have an ID');
const plainText = await this.encryptionService_.decryptMasterKeyContent(key, password);
return { id: key.id, plainText };
}
}