mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
This commit is contained in:
parent
ac2258769a
commit
0bfa28d795
@ -207,7 +207,13 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) =>
|
|||||||
setTimeLocale(Setting.value('locale'));
|
setTimeLocale(Setting.value('locale'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((action.type === 'SETTING_UPDATE_ONE' && (action.key.indexOf('encryption.') === 0)) || (action.type === 'SETTING_UPDATE_ALL')) {
|
// Like the desktop and CLI apps, we run this whenever the sync target properties change.
|
||||||
|
// Previously, this only ran when encryption was enabled/disabled. However, after fetching
|
||||||
|
// a new key, this needs to run and so we run it when the sync target info changes.
|
||||||
|
if (
|
||||||
|
(action.type === 'SETTING_UPDATE_ONE' && (action.key === 'syncInfoCache' || action.key.startsWith('encryption.')))
|
||||||
|
|| action.type === 'SETTING_UPDATE_ALL'
|
||||||
|
) {
|
||||||
await loadMasterKeysFromSettings(EncryptionService.instance());
|
await loadMasterKeysFromSettings(EncryptionService.instance());
|
||||||
void DecryptionWorker.instance().scheduleStart();
|
void DecryptionWorker.instance().scheduleStart();
|
||||||
const loadedMasterKeyIds = EncryptionService.instance().loadedMasterKeyIds();
|
const loadedMasterKeyIds = EncryptionService.instance().loadedMasterKeyIds();
|
||||||
|
@ -506,6 +506,13 @@ export default class BaseItem extends BaseModel {
|
|||||||
masterKeyId: share && share.master_key_id ? share.master_key_id : '',
|
masterKeyId: share && share.master_key_id ? share.master_key_id : '',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error.code === 'masterKeyNotLoaded' && error.masterKeyId) {
|
||||||
|
this.dispatch?.({
|
||||||
|
type: 'MASTERKEY_ADD_NOT_LOADED',
|
||||||
|
id: error.masterKeyId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const msg = [`Could not encrypt item ${item.id}`];
|
const msg = [`Could not encrypt item ${item.id}`];
|
||||||
if (error && error.message) msg.push(error.message);
|
if (error && error.message) msg.push(error.message);
|
||||||
const newError = new Error(msg.join(': '));
|
const newError = new Error(msg.join(': '));
|
||||||
|
@ -52,6 +52,12 @@ export interface EncryptOptions {
|
|||||||
masterKeyId?: string;
|
masterKeyId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetPasswordCallback = ()=> string|Promise<string>;
|
||||||
|
interface EncryptedMasterKey {
|
||||||
|
updatedTime: number;
|
||||||
|
decrypt: ()=> Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
export default class EncryptionService {
|
export default class EncryptionService {
|
||||||
|
|
||||||
public static instance_: EncryptionService = null;
|
public static instance_: EncryptionService = null;
|
||||||
@ -73,7 +79,8 @@ export default class EncryptionService {
|
|||||||
// So making the block 10 times smaller make it 100 times faster! So for now using 5KB. This can be
|
// So making the block 10 times smaller make it 100 times faster! So for now using 5KB. This can be
|
||||||
// changed easily since the chunk size is incorporated into the encrypted data.
|
// changed easily since the chunk size is incorporated into the encrypted data.
|
||||||
private chunkSize_ = 5000;
|
private chunkSize_ = 5000;
|
||||||
private decryptedMasterKeys_: Record<string, DecryptedMasterKey> = {};
|
private encryptedMasterKeys_: Map<string, EncryptedMasterKey> = new Map();
|
||||||
|
private decryptedMasterKeys_: Map<string, DecryptedMasterKey> = new Map();
|
||||||
public defaultEncryptionMethod_ = EncryptionMethod.SJCL1a; // public because used in tests
|
public defaultEncryptionMethod_ = EncryptionMethod.SJCL1a; // public because used in tests
|
||||||
private defaultMasterKeyEncryptionMethod_ = EncryptionMethod.SJCL4;
|
private defaultMasterKeyEncryptionMethod_ = EncryptionMethod.SJCL4;
|
||||||
|
|
||||||
@ -96,7 +103,7 @@ export default class EncryptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public loadedMasterKeysCount() {
|
public loadedMasterKeysCount() {
|
||||||
return Object.keys(this.decryptedMasterKeys_).length;
|
return this.loadedMasterKeyIds().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public chunkSize() {
|
public chunkSize() {
|
||||||
@ -123,41 +130,78 @@ export default class EncryptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isMasterKeyLoaded(masterKey: MasterKeyEntity) {
|
public isMasterKeyLoaded(masterKey: MasterKeyEntity) {
|
||||||
const d = this.decryptedMasterKeys_[masterKey.id];
|
if (this.encryptedMasterKeys_.get(masterKey.id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const d = this.decryptedMasterKeys_.get(masterKey.id);
|
||||||
if (!d) return false;
|
if (!d) return false;
|
||||||
return d.updatedTime === masterKey.updated_time;
|
return d.updatedTime === masterKey.updated_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadMasterKey(model: MasterKeyEntity, password: string, makeActive = false) {
|
public async loadMasterKey(model: MasterKeyEntity, getPassword: string|GetPasswordCallback, makeActive = false) {
|
||||||
if (!model.id) throw new Error('Master key does not have an ID - save it first');
|
if (!model.id) throw new Error('Master key does not have an ID - save it first');
|
||||||
|
|
||||||
logger.info(`Loading master key: ${model.id}. Make active:`, makeActive);
|
const loadKey = async () => {
|
||||||
|
logger.info(`Loading master key: ${model.id}. Make active:`, makeActive);
|
||||||
|
|
||||||
this.decryptedMasterKeys_[model.id] = {
|
const password = typeof getPassword === 'string' ? getPassword : (await getPassword());
|
||||||
plainText: await this.decryptMasterKeyContent(model, password),
|
if (!password) {
|
||||||
updatedTime: model.updated_time,
|
logger.info(`Loading master key ${model.id} failed. No valid password found.`);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.decryptedMasterKeys_.set(model.id, {
|
||||||
|
plainText: await this.decryptMasterKeyContent(model, password),
|
||||||
|
updatedTime: model.updated_time,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (makeActive) this.setActiveMasterKeyId(model.id);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`Cannot load master key ${model.id}. Invalid password?`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.encryptedMasterKeys_.delete(model.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (makeActive) this.setActiveMasterKeyId(model.id);
|
if (!makeActive) {
|
||||||
|
this.encryptedMasterKeys_.set(model.id, {
|
||||||
|
decrypt: loadKey,
|
||||||
|
updatedTime: model.updated_time,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await loadKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadMasterKey(model: MasterKeyEntity) {
|
public unloadMasterKey(model: MasterKeyEntity) {
|
||||||
delete this.decryptedMasterKeys_[model.id];
|
this.decryptedMasterKeys_.delete(model.id);
|
||||||
|
this.encryptedMasterKeys_.delete(model.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadedMasterKey(id: string) {
|
public async loadedMasterKey(id: string) {
|
||||||
if (!this.decryptedMasterKeys_[id]) {
|
const cachedKey = this.decryptedMasterKeys_.get(id);
|
||||||
|
if (cachedKey) return cachedKey;
|
||||||
|
|
||||||
|
const decryptCallback = this.encryptedMasterKeys_.get(id);
|
||||||
|
if (decryptCallback) {
|
||||||
|
// TODO: Handle invalid password errors?
|
||||||
|
await decryptCallback.decrypt();
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = this.decryptedMasterKeys_.get(id);
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const error: any = new Error(`Master key is not loaded: ${id}`);
|
const error: any = new Error(`Master key is not loaded: ${id}`);
|
||||||
error.code = 'masterKeyNotLoaded';
|
error.code = 'masterKeyNotLoaded';
|
||||||
error.masterKeyId = id;
|
error.masterKeyId = id;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
return this.decryptedMasterKeys_[id];
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadedMasterKeyIds() {
|
public loadedMasterKeyIds() {
|
||||||
return Object.keys(this.decryptedMasterKeys_);
|
return [...this.decryptedMasterKeys_.keys(), ...this.encryptedMasterKeys_.keys()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public fsDriver() {
|
public fsDriver() {
|
||||||
@ -430,7 +474,7 @@ export default class EncryptionService {
|
|||||||
|
|
||||||
const method = options.encryptionMethod;
|
const method = options.encryptionMethod;
|
||||||
const masterKeyId = options.masterKeyId ? options.masterKeyId : this.activeMasterKeyId();
|
const masterKeyId = options.masterKeyId ? options.masterKeyId : this.activeMasterKeyId();
|
||||||
const masterKeyPlainText = this.loadedMasterKey(masterKeyId).plainText;
|
const masterKeyPlainText = (await this.loadedMasterKey(masterKeyId)).plainText;
|
||||||
|
|
||||||
const header = {
|
const header = {
|
||||||
encryptionMethod: method,
|
encryptionMethod: method,
|
||||||
@ -465,7 +509,7 @@ export default class EncryptionService {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const header: any = await this.decodeHeaderSource_(source);
|
const header: any = await this.decodeHeaderSource_(source);
|
||||||
const masterKeyPlainText = this.loadedMasterKey(header.masterKeyId).plainText;
|
const masterKeyPlainText = (await this.loadedMasterKey(header.masterKeyId)).plainText;
|
||||||
|
|
||||||
let doneSize = 0;
|
let doneSize = 0;
|
||||||
|
|
||||||
|
@ -142,14 +142,10 @@ export async function loadMasterKeysFromSettings(service: EncryptionService) {
|
|||||||
const mk = masterKeys[i];
|
const mk = masterKeys[i];
|
||||||
if (service.isMasterKeyLoaded(mk)) continue;
|
if (service.isMasterKeyLoaded(mk)) continue;
|
||||||
|
|
||||||
const password = await findMasterKeyPassword(service, mk);
|
await service.loadMasterKey(mk, async () => {
|
||||||
if (!password) continue;
|
const password = await findMasterKeyPassword(service, mk);
|
||||||
|
return password;
|
||||||
try {
|
}, activeMasterKeyId === mk.id);
|
||||||
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()}`);
|
logger.info(`Loaded master keys: ${service.loadedMasterKeysCount()}`);
|
||||||
|
Loading…
Reference in New Issue
Block a user