Verify individual messages via cross-signing

Fixes #11880
This commit is contained in:
Zoe 2020-01-20 15:16:41 +00:00
parent a984f9acf3
commit f77eb07849
3 changed files with 61 additions and 12 deletions

View file

@ -367,6 +367,11 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
opacity: 1; opacity: 1;
} }
.mx_EventTile_e2eIcon_userVerified {
background-image: url('$(res)/img/e2e/normal.svg');
opacity: 0.5;
}
.mx_EventTile_e2eIcon_unencrypted { .mx_EventTile_e2eIcon_unencrypted {
background-image: url('$(res)/img/e2e/warning.svg'); background-image: url('$(res)/img/e2e/warning.svg');
opacity: 1; opacity: 1;
@ -415,7 +420,8 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
} }
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line { .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_userVerified .mx_EventTile_line {
padding-left: 60px; padding-left: 60px;
} }
@ -427,8 +433,13 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
border-left: $e2e-unverified-color 5px solid; border-left: $e2e-unverified-color 5px solid;
} }
.mx_EventTile:hover.mx_EventTile_userVerified .mx_EventTile_line {
border-left: $e2e-userVerified-color 5px solid;
}
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line { .mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_userVerified.mx_EventTile_info .mx_EventTile_line {
padding-left: 78px; padding-left: 78px;
} }
@ -439,14 +450,16 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp { .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_userVerified .mx_EventTile_line > a > .mx_MessageTimestamp {
left: 3px; left: 3px;
width: auto; width: auto;
} }
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > .mx_EventTile_e2eIcon, .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > .mx_EventTile_e2eIcon,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon { .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon,
.mx_EventTile:hover.mx_EventTile_userVerified .mx_EventTile_line > .mx_EventTile_e2eIcon {
display: block; display: block;
left: 41px; left: 41px;
} }

View file

@ -224,6 +224,7 @@ $copy-button-url: "$(res)/img/icon_copy_message.svg";
// e2e // e2e
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
$e2e-userVerified-color: #e8bf37;
$e2e-unverified-color: #e8bf37; $e2e-unverified-color: #e8bf37;
$e2e-warning-color: #ba6363; $e2e-warning-color: #ba6363;

View file

@ -235,6 +235,7 @@ export default createReactClass({
this._suppressReadReceiptAnimation = false; this._suppressReadReceiptAnimation = false;
const client = this.context; const client = this.context;
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged); client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.on("userTrustStatusChanged", this.onUserVerificationChanged);
this.props.mxEvent.on("Event.decrypted", this._onDecrypted); this.props.mxEvent.on("Event.decrypted", this._onDecrypted);
if (this.props.showReactions) { if (this.props.showReactions) {
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated); this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
@ -260,6 +261,7 @@ export default createReactClass({
componentWillUnmount: function() { componentWillUnmount: function() {
const client = this.context; const client = this.context;
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted); this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
if (this.props.showReactions) { if (this.props.showReactions) {
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated); this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
@ -282,18 +284,42 @@ export default createReactClass({
} }
}, },
onUserVerificationChanged: function(userId, _trustStatus) {
if (userId === this.props.mxEvent.getSender()) {
this._verifyEvent(this.props.mxEvent);
}
},
_verifyEvent: async function(mxEvent) { _verifyEvent: async function(mxEvent) {
if (!mxEvent.isEncrypted()) { if (!mxEvent.isEncrypted()) {
return; return;
} }
// If we directly trust the device, short-circuit here
const verified = await this.context.isEventSenderVerified(mxEvent); const verified = await this.context.isEventSenderVerified(mxEvent);
if (verified) {
this.setState({
verified: "verified"
}, () => {
// Decryption may have caused a change in size
this.props.onHeightChanged();
});
return;
}
const eventSenderTrust = await this.context.checkEventSenderTrust(mxEvent);
if (!eventSenderTrust) {
// We cannot find the device. Instead, we have to verify the user.
const userTrust = await this.context.checkUserTrust(mxEvent.getSender());
this.setState({
verified: userTrust.isVerified() ? "user-verified": "warning",
}, this.props.onHeightChanged); // Decryption may have cause a change in size
return;
}
this.setState({ this.setState({
verified: verified, verified: eventSenderTrust.isVerified() ? "verified" : "warning",
}, () => { }, this.props.onHeightChanged); // Decryption may have caused a change in size
// Decryption may have caused a change in size
this.props.onHeightChanged();
});
}, },
_propsEqual: function(objA, objB) { _propsEqual: function(objA, objB) {
@ -473,8 +499,10 @@ export default createReactClass({
// event is encrypted, display padlock corresponding to whether or not it is verified // event is encrypted, display padlock corresponding to whether or not it is verified
if (ev.isEncrypted()) { if (ev.isEncrypted()) {
if (this.state.verified) { if (this.state.verified === "verified") {
return; // no icon for verified return; // no icon for verified
} else if (this.state.verified === "user-verified") {
return (<E2ePadlockUserVerified />);
} else { } else {
return (<E2ePadlockUnverified />); return (<E2ePadlockUnverified />);
} }
@ -604,8 +632,9 @@ export default createReactClass({
mx_EventTile_last: this.props.last, mx_EventTile_last: this.props.last,
mx_EventTile_contextual: this.props.contextual, mx_EventTile_contextual: this.props.contextual,
mx_EventTile_actionBarFocused: this.state.actionBarFocused, mx_EventTile_actionBarFocused: this.state.actionBarFocused,
mx_EventTile_verified: !isBubbleMessage && this.state.verified === true, mx_EventTile_verified: !isBubbleMessage && this.state.verified === "verified",
mx_EventTile_unverified: !isBubbleMessage && this.state.verified === false, mx_EventTile_unverified: !isBubbleMessage && this.state.verified === "warning",
mx_EventTile_userVerified: !isBubbleMessage && this.state.verified === "user-verified",
mx_EventTile_bad: isEncryptionFailure, mx_EventTile_bad: isEncryptionFailure,
mx_EventTile_emote: msgtype === 'm.emote', mx_EventTile_emote: msgtype === 'm.emote',
mx_EventTile_redacted: isRedacted, mx_EventTile_redacted: isRedacted,
@ -901,6 +930,12 @@ function E2ePadlockUnencrypted(props) {
); );
} }
function E2ePadlockUserVerified(props) {
return (
<E2ePadlock title={_t("Encrypted by a deleted device")} icon="userVerified" {...props} />
);
}
class E2ePadlock extends React.Component { class E2ePadlock extends React.Component {
static propTypes = { static propTypes = {
icon: PropTypes.string.isRequired, icon: PropTypes.string.isRequired,