1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-26 22:41:17 +02:00

All: Security: Added way to upgrade master key encryption and sync target encryption

This commit is contained in:
Laurent Cozic
2020-03-13 17:42:50 +00:00
parent 3917e3469d
commit f4958de885
17 changed files with 423 additions and 164 deletions

View File

@@ -395,6 +395,10 @@ class ConfigScreenComponent extends BaseScreenComponent {
);
} else if (md.type == Setting.TYPE_INT) {
const unitLabel = md.unitLabel ? md.unitLabel(value) : value;
// Note: Do NOT add the minimumTrackTintColor and maximumTrackTintColor props
// on the Slider as they are buggy and can crash the app on certain devices.
// https://github.com/laurent22/joplin/issues/2733
// https://github.com/react-native-community/react-native-slider/issues/161
return (
<View key={key} style={this.styles().settingContainer}>
<Text key="label" style={this.styles().settingText}>
@@ -406,7 +410,6 @@ class ConfigScreenComponent extends BaseScreenComponent {
</View>
</View>
);
// minimumTrackTintColor={theme.color} maximumTrackTintColor={theme.color}
} else if (md.type == Setting.TYPE_STRING) {
return (
<View key={key} style={this.styles().settingContainer}>

View File

@@ -31,10 +31,6 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
this.styles_ = {};
}
componentDidMount() {
this.isMounted_ = true;
}
componentWillUnmount() {
this.isMounted_ = false;
}
@@ -47,12 +43,13 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
return shared.refreshStats(this);
}
UNSAFE_componentWillMount() {
this.initState(this.props);
componentDidMount() {
this.isMounted_ = true;
shared.componentDidMount(this);
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.initState(nextProps);
componentDidUpdate(prevProps) {
shared.componentDidUpdate(this, prevProps);
}
async checkPasswords() {
@@ -110,7 +107,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
return shared.onPasswordChange(this, mk, text);
};
const password = this.state.passwords[mk.id] ? this.state.passwords[mk.id] : '';
const password = this.props.passwords[mk.id] ? this.props.passwords[mk.id] : '';
const passwordOk = this.state.passwordChecks[mk.id] === true ? '✔' : '❌';
const inputStyle = { flex: 1, marginRight: 10, color: theme.color };
@@ -196,7 +193,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
render() {
const theme = themeStyle(this.props.theme);
const masterKeys = this.state.masterKeys;
const masterKeys = this.props.masterKeys;
const decryptedItemsInfo = this.props.encryptionEnabled ? <Text style={this.styles().normalText}>{shared.decryptedStatText(this)}</Text> : null;
const mkComps = [];

View File

@@ -2,13 +2,13 @@ const EncryptionService = require('lib/services/EncryptionService');
const { _ } = require('lib/locale.js');
const BaseItem = require('lib/models/BaseItem.js');
const Setting = require('lib/models/Setting.js');
const MasterKey = require('lib/models/MasterKey.js');
const { reg } = require('lib/registry.js');
const shared = {};
shared.constructor = function(comp) {
comp.state = {
masterKeys: [],
passwords: {},
passwordChecks: {},
stats: {
encrypted: null,
@@ -17,47 +17,86 @@ shared.constructor = function(comp) {
};
comp.isMounted_ = false;
comp.refreshStatsIID_ = null;
};
shared.initState = function(comp, props) {
comp.setState(
{
masterKeys: props.masterKeys,
passwords: props.passwords ? props.passwords : {},
},
() => {
comp.checkPasswords();
}
);
comp.refreshStats();
if (comp.refreshStatsIID_) {
clearInterval(comp.refreshStatsIID_);
comp.refreshStatsIID_ = null;
}
comp.refreshStatsIID_ = setInterval(() => {
if (!comp.isMounted_) {
clearInterval(comp.refreshStatsIID_);
comp.refreshStatsIID_ = null;
return;
}
comp.refreshStats();
}, 3000);
shared.refreshStatsIID_ = null;
};
shared.refreshStats = async function(comp) {
const stats = await BaseItem.encryptedItemsStats();
comp.setState({ stats: stats });
comp.setState({
stats: stats,
});
};
shared.reencryptData = async function() {
const ok = confirm(_('Please confirm that you would like to reencrypt your complete database.'));
if (!ok) return;
await BaseItem.forceSyncAll();
reg.waitForSyncFinishedThenSync();
Setting.setValue('encryption.shouldReencrypt', Setting.SHOULD_REENCRYPT_NO);
alert(_('Your data is going to be reencrypted and synced again.'));
};
shared.dontReencryptData = function() {
Setting.setValue('encryption.shouldReencrypt', Setting.SHOULD_REENCRYPT_NO);
};
shared.upgradeMasterKey = async function(comp, masterKey) {
const passwordCheck = comp.state.passwordChecks[masterKey.id];
if (!passwordCheck) {
alert(_('Please enter your password in the master key list below before upgrading the key.'));
return;
}
try {
const password = comp.props.passwords[masterKey.id];
const newMasterKey = await EncryptionService.instance().upgradeMasterKey(masterKey, password);
await MasterKey.save(newMasterKey);
reg.waitForSyncFinishedThenSync();
alert(_('The master key has been upgraded successfully!'));
} catch (error) {
alert(_('Could not upgrade master key: %s', error.message));
}
};
shared.componentDidMount = async function(comp) {
shared.componentDidUpdate(comp);
shared.refreshStats(comp);
if (shared.refreshStatsIID_) {
clearInterval(shared.refreshStatsIID_);
shared.refreshStatsIID_ = null;
}
shared.refreshStatsIID_ = setInterval(() => {
if (!comp.isMounted_) {
clearInterval(shared.refreshStatsIID_);
shared.refreshStatsIID_ = null;
return;
}
shared.refreshStats(comp);
}, 3000);
};
shared.componentDidUpdate = async function(comp, prevProps = null) {
if (!prevProps || comp.props.masterKeys !== prevProps.masterKeys || comp.props.passwords !== prevProps.passwords) {
comp.checkPasswords();
}
};
shared.componentWillUnmount = function() {
if (shared.refreshStatsIID_) {
clearInterval(shared.refreshStatsIID_);
shared.refreshStatsIID_ = null;
}
};
shared.checkPasswords = async function(comp) {
const passwordChecks = Object.assign({}, comp.state.passwordChecks);
for (let i = 0; i < comp.state.masterKeys.length; i++) {
const mk = comp.state.masterKeys[i];
const password = comp.state.passwords[mk.id];
for (let i = 0; i < comp.props.masterKeys.length; i++) {
const mk = comp.props.masterKeys[i];
const password = comp.props.passwords[mk.id];
const ok = password ? await EncryptionService.instance().checkMasterKeyPassword(mk, password) : false;
passwordChecks[mk.id] = ok;
}
@@ -72,7 +111,7 @@ shared.decryptedStatText = function(comp) {
};
shared.onSavePasswordClick = function(comp, mk) {
const password = comp.state.passwords[mk.id];
const password = comp.props.passwords[mk.id];
if (!password) {
Setting.deleteObjectKey('encryption.passwordCache', mk.id);
} else {
@@ -83,7 +122,7 @@ shared.onSavePasswordClick = function(comp, mk) {
};
shared.onPasswordChange = function(comp, mk, password) {
const passwords = comp.state.passwords;
const passwords = comp.props.passwords;
passwords[mk.id] = password;
comp.setState({ passwords: passwords });
};