2020-09-11 14:31:38 +00:00
|
|
|
import EJSON from 'ejson';
|
2021-09-13 20:41:05 +00:00
|
|
|
import { put, select, takeLatest } from 'redux-saga/effects';
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
import { ENCRYPTION } from '../actions/actionsTypes';
|
2021-01-20 17:34:01 +00:00
|
|
|
import { encryptionSet } from '../actions/encryption';
|
2020-09-11 14:31:38 +00:00
|
|
|
import { Encryption } from '../lib/encryption';
|
|
|
|
import Navigation from '../lib/Navigation';
|
2022-04-04 19:15:29 +00:00
|
|
|
import { E2E_BANNER_TYPE, E2E_PRIVATE_KEY, E2E_PUBLIC_KEY, E2E_RANDOM_PASSWORD_KEY } from '../lib/constants';
|
2020-09-11 14:31:38 +00:00
|
|
|
import database from '../lib/database';
|
|
|
|
import RocketChat from '../lib/rocketchat';
|
|
|
|
import UserPreferences from '../lib/userPreferences';
|
|
|
|
import { getUserSelector } from '../selectors/login';
|
|
|
|
import { showErrorAlert } from '../utils/info';
|
|
|
|
import I18n from '../i18n';
|
|
|
|
import log from '../utils/log';
|
|
|
|
|
2020-11-04 16:53:44 +00:00
|
|
|
const getServer = state => state.share.server.server || state.server.server;
|
2020-09-23 17:16:04 +00:00
|
|
|
const getE2eEnable = state => state.settings.E2E_Enable;
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
const handleEncryptionInit = function* handleEncryptionInit() {
|
|
|
|
try {
|
|
|
|
const server = yield select(getServer);
|
|
|
|
const user = yield select(getUserSelector);
|
2020-09-23 17:16:04 +00:00
|
|
|
const E2E_Enable = yield select(getE2eEnable);
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
// Fetch server info to check E2E enable
|
|
|
|
const serversDB = database.servers;
|
2021-02-26 16:25:51 +00:00
|
|
|
const serversCollection = serversDB.get('servers');
|
2020-09-23 17:16:04 +00:00
|
|
|
let serverInfo;
|
|
|
|
try {
|
|
|
|
serverInfo = yield serversCollection.find(server);
|
|
|
|
} catch {
|
|
|
|
// Server not found
|
|
|
|
}
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
// If E2E is disabled on server, skip
|
2020-09-23 17:16:04 +00:00
|
|
|
if (!serverInfo?.E2E_Enable && !E2E_Enable) {
|
2020-09-11 14:31:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch stored private e2e key for this server
|
2022-03-09 19:41:26 +00:00
|
|
|
const storedPrivateKey = UserPreferences.getString(`${server}-${E2E_PRIVATE_KEY}`);
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
// Fetch server stored e2e keys
|
|
|
|
const keys = yield RocketChat.e2eFetchMyKeys();
|
|
|
|
|
|
|
|
// A private key was received from the server, but it's not saved locally yet
|
|
|
|
// Show the banner asking for the password
|
|
|
|
if (!storedPrivateKey && keys?.privateKey) {
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet(false, E2E_BANNER_TYPE.REQUEST_PASSWORD));
|
2020-09-11 14:31:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the user has a private key stored, but never entered the password
|
2022-03-09 19:41:26 +00:00
|
|
|
const storedRandomPassword = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
|
|
|
|
2020-09-11 14:31:38 +00:00
|
|
|
if (storedRandomPassword) {
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
|
2020-09-11 14:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch stored public e2e key for this server
|
2022-03-09 19:41:26 +00:00
|
|
|
let storedPublicKey = UserPreferences.getString(`${server}-${E2E_PUBLIC_KEY}`);
|
|
|
|
|
2020-09-11 14:31:38 +00:00
|
|
|
// Prevent parse undefined
|
|
|
|
if (storedPublicKey) {
|
|
|
|
storedPublicKey = EJSON.parse(storedPublicKey);
|
|
|
|
}
|
|
|
|
|
2021-04-01 19:27:46 +00:00
|
|
|
if (storedPublicKey && storedPrivateKey && !storedRandomPassword) {
|
2020-09-11 14:31:38 +00:00
|
|
|
// Persist these keys
|
|
|
|
yield Encryption.persistKeys(server, storedPublicKey, storedPrivateKey);
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet(true));
|
2020-09-11 14:31:38 +00:00
|
|
|
} else {
|
|
|
|
// Create new keys since the user doesn't have any
|
|
|
|
yield Encryption.createKeys(user.id, server);
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
|
2020-09-11 14:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Decrypt all pending messages/subscriptions
|
2020-09-24 18:34:13 +00:00
|
|
|
Encryption.initialize(user.id);
|
2020-09-11 14:31:38 +00:00
|
|
|
} catch (e) {
|
|
|
|
log(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleEncryptionStop = function* handleEncryptionStop() {
|
|
|
|
// Hide encryption banner
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet());
|
2020-09-11 14:31:38 +00:00
|
|
|
// Stop Encryption client
|
|
|
|
Encryption.stop();
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password }) {
|
|
|
|
try {
|
|
|
|
const server = yield select(getServer);
|
|
|
|
const user = yield select(getUserSelector);
|
|
|
|
|
|
|
|
// Fetch server stored e2e keys
|
|
|
|
const keys = yield RocketChat.e2eFetchMyKeys();
|
|
|
|
|
|
|
|
const publicKey = EJSON.parse(keys?.publicKey);
|
|
|
|
|
|
|
|
// Decode the current server key
|
|
|
|
const privateKey = yield Encryption.decodePrivateKey(keys?.privateKey, password, user.id);
|
|
|
|
|
|
|
|
// Persist these decrypted keys
|
|
|
|
yield Encryption.persistKeys(server, publicKey, privateKey);
|
|
|
|
|
|
|
|
// Decrypt all pending messages/subscriptions
|
2020-09-24 18:34:13 +00:00
|
|
|
Encryption.initialize(user.id);
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
// Hide encryption banner
|
2021-01-20 17:34:01 +00:00
|
|
|
yield put(encryptionSet(true));
|
2020-09-11 14:31:38 +00:00
|
|
|
|
|
|
|
Navigation.back();
|
|
|
|
} catch {
|
|
|
|
// Can't decrypt user private key
|
|
|
|
showErrorAlert(I18n.t('Encryption_error_desc'), I18n.t('Encryption_error_title'));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const root = function* root() {
|
|
|
|
yield takeLatest(ENCRYPTION.INIT, handleEncryptionInit);
|
|
|
|
yield takeLatest(ENCRYPTION.STOP, handleEncryptionStop);
|
|
|
|
yield takeLatest(ENCRYPTION.DECODE_KEY, handleEncryptionDecodeKey);
|
|
|
|
};
|
|
|
|
export default root;
|