First working version of SAS
This commit is contained in:
parent
2a20d9d67e
commit
1d2538a7bc
6 changed files with 517 additions and 49 deletions
|
@ -29,6 +29,7 @@ import SettingsStore from './settings/SettingsStore';
|
|||
import MatrixActionCreators from './actions/MatrixActionCreators';
|
||||
import {phasedRollOutExpiredForUser} from "./PhasedRollOut";
|
||||
import Modal from './Modal';
|
||||
import {verificationMethods} from 'matrix-js-sdk/lib/crypto';
|
||||
|
||||
interface MatrixClientCreds {
|
||||
homeserverUrl: string,
|
||||
|
@ -184,6 +185,7 @@ class MatrixClientPeg {
|
|||
deviceId: creds.deviceId,
|
||||
timelineSupport: true,
|
||||
forceTURN: SettingsStore.getValue('webRtcForceTURN', false),
|
||||
verificationMethods: [verificationMethods.SAS]
|
||||
};
|
||||
|
||||
this.matrixClient = createMatrixClient(opts, useIndexedDb);
|
||||
|
|
|
@ -1277,6 +1277,7 @@ export default React.createClass({
|
|||
this.firstSyncComplete = false;
|
||||
this.firstSyncPromise = Promise.defer();
|
||||
const cli = MatrixClientPeg.get();
|
||||
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');
|
||||
|
||||
// Allow the JS SDK to reap timeline events. This reduces the amount of
|
||||
// memory consumed as the JS SDK stores multiple distinct copies of room
|
||||
|
@ -1476,6 +1477,12 @@ export default React.createClass({
|
|||
}
|
||||
});
|
||||
|
||||
cli.on("crypto.verification.start", (verifier) => {
|
||||
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
|
||||
verifier,
|
||||
});
|
||||
});
|
||||
|
||||
// Fire the tinter right on startup to ensure the default theme is applied
|
||||
// A later sync can/will correct the tint to be the right value for the user
|
||||
const colorScheme = SettingsStore.getValue("roomColor");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -21,58 +22,273 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
|||
import sdk from '../../../index';
|
||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
import {verificationMethods} from 'matrix-js-sdk/lib/crypto';
|
||||
import {renderSasWaitAccept} from '../../../sas_ui';
|
||||
|
||||
export default function DeviceVerifyDialog(props) {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const MODE_LEGACY = 'legacy';
|
||||
const MODE_SAS = 'sas';
|
||||
|
||||
const key = FormattingUtils.formatCryptoKey(props.device.getFingerprint());
|
||||
const body = (
|
||||
<div>
|
||||
<p>
|
||||
{ _t("To verify that this device can be trusted, please contact its " +
|
||||
"owner using some other means (e.g. in person or a phone call) " +
|
||||
"and ask them whether the key they see in their User Settings " +
|
||||
"for this device matches the key below:") }
|
||||
</p>
|
||||
<div className="mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>{ _t("Device name") }:</label> <span>{ props.device.getDisplayName() }</span></li>
|
||||
<li><label>{ _t("Device ID") }:</label> <span><code>{ props.device.deviceId }</code></span></li>
|
||||
<li><label>{ _t("Device key") }:</label> <span><code><b>{ key }</b></code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
{ _t("If it matches, press the verify button below. " +
|
||||
"If it doesn't, then someone else is intercepting this device " +
|
||||
"and you probably want to press the blacklist button instead.") }
|
||||
</p>
|
||||
<p>
|
||||
{ _t("In future this verification process will be more sophisticated.") }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
const PHASE_START = 0;
|
||||
const PHASE_WAIT_FOR_PARTNER_TO_ACCEPT = 1;
|
||||
const PHASE_SHOW_SAS = 2;
|
||||
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 3;
|
||||
const PHASE_VERIFIED = 4;
|
||||
const PHASE_CANCELLED = 5;
|
||||
|
||||
function onFinished(confirm) {
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
props.userId, props.device.deviceId, true,
|
||||
);
|
||||
}
|
||||
props.onFinished(confirm);
|
||||
export default class DeviceVerifyDialog extends React.Component {
|
||||
static propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._verifier = null;
|
||||
this._showSasEvent = null;
|
||||
this.state = {
|
||||
phase: PHASE_START,
|
||||
mode: SettingsStore.isFeatureEnabled("feature_sas") ? MODE_SAS : MODE_LEGACY,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title={_t("Verify device")}
|
||||
description={body}
|
||||
button={_t("I verify that the keys match")}
|
||||
onFinished={onFinished}
|
||||
/>
|
||||
);
|
||||
componentWillUnmount() {
|
||||
if (this._verifier) {
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier.cancel('User cancel');
|
||||
}
|
||||
}
|
||||
|
||||
_onSwitchToLegacyClick = () => {
|
||||
this.setState({mode: MODE_LEGACY});
|
||||
}
|
||||
|
||||
_onSwitchToSasClick = () => {
|
||||
this.setState({mode: MODE_SAS});
|
||||
}
|
||||
|
||||
_onCancelClick = () => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onLegacyFinished = (confirm) => {
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
this.props.userId, this.props.device.deviceId, true,
|
||||
);
|
||||
}
|
||||
this.props.onFinished(confirm);
|
||||
}
|
||||
|
||||
_onSasRequestClick = () => {
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_ACCEPT,
|
||||
});
|
||||
this._verifier = MatrixClientPeg.get().beginKeyVerification(
|
||||
verificationMethods.SAS, this.props.userId, this.props.device.deviceId,
|
||||
);
|
||||
this._verifier.on('show_sas', this._onVerifierShowSas);
|
||||
this._verifier.verify().then(() => {
|
||||
this.setState({phase: PHASE_VERIFIED});
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier = null;
|
||||
}).catch((e) => {
|
||||
console.log("Verification failed", e);
|
||||
this.setState({
|
||||
phase: PHASE_CANCELLED,
|
||||
});
|
||||
this._verifier = null;
|
||||
});
|
||||
}
|
||||
|
||||
_onSasMatchesClick = () => {
|
||||
this._showSasEvent.confirm();
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
|
||||
});
|
||||
}
|
||||
|
||||
_onVerifiedDoneClick = () => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
|
||||
_onVerifierShowSas = (e) => {
|
||||
this._showSasEvent = e;
|
||||
this.setState({
|
||||
phase: PHASE_SHOW_SAS,
|
||||
});
|
||||
}
|
||||
|
||||
_renderSasVerification() {
|
||||
let body;
|
||||
switch (this.state.phase) {
|
||||
case PHASE_START:
|
||||
body = this._renderSasVerificationPhaseStart();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_ACCEPT:
|
||||
//body = this._renderSasVerificationPhaseWaitForPartnerToAccept();
|
||||
body = renderSasWaitAccept(this.props.userId);
|
||||
break;
|
||||
case PHASE_SHOW_SAS:
|
||||
body = this._renderSasVerificationPhaseShowSas();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
|
||||
body = this._renderSasVerificationPhaseWaitForPartnerToConfirm();
|
||||
break;
|
||||
case PHASE_VERIFIED:
|
||||
body = this._renderSasVerificationPhaseVerified();
|
||||
break;
|
||||
case PHASE_CANCELLED:
|
||||
body = this._renderSasVerificationPhaseCancelled();
|
||||
break;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("Verify device")}
|
||||
onFinished={this._onCancelClick}
|
||||
>
|
||||
{body}
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseStart() {
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return (
|
||||
<div>
|
||||
<AccessibleButton
|
||||
element="span" className="mx_linkButton" onClick={this._onSwitchToLegacyClick}
|
||||
>
|
||||
{_t("Use Legacy Verification (for older clients)")}
|
||||
</AccessibleButton>
|
||||
<p>
|
||||
{ _t("Do clicky clicky button press request verify user send to do.") }
|
||||
</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Send Verification Request')}
|
||||
hasCancel={true}
|
||||
onPrimaryButtonClick={this._onSasRequestClick}
|
||||
onCancel={this._onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseShowSas() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t(
|
||||
"Verify this user by confirming the following number appears on their screen"
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"For maximum security, we reccommend you do this in person or use another " +
|
||||
"trusted means of communication"
|
||||
)}</p>
|
||||
<pre>{this._showSasEvent.sas}</pre>
|
||||
<DialogButtons onPrimaryButtonClick={this._onSasMatchesClick}
|
||||
primaryButton={_t("This Matches")}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancelClick}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseWaitForPartnerToConfirm() {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
return <div>
|
||||
<Spinner />
|
||||
<p>{_t(
|
||||
"Waiting for %(userId)s to confirm...", {userId: this.props.userId},
|
||||
)}</p>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseVerified() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t("Verification complete!")}</p>
|
||||
<DialogButtons onPrimaryButtonClick={this._onVerifiedDoneClick}
|
||||
primaryButton={_t("Done")}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseCancelled() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{_t(
|
||||
"%(userId)s cancelled the verification.", {userId: this.props.userId},
|
||||
)}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Cancel')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this._onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderLegacyVerification() {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
|
||||
const key = FormattingUtils.formatCryptoKey(this.props.device.getFingerprint());
|
||||
const body = (
|
||||
<div>
|
||||
<AccessibleButton
|
||||
element="span" className="mx_linkButton" onClick={this._onSwitchToSasClick}
|
||||
>
|
||||
{_t("Use two-way text verification")}
|
||||
</AccessibleButton>
|
||||
<p>
|
||||
{ _t("To verify that this device can be trusted, please contact its " +
|
||||
"owner using some other means (e.g. in person or a phone call) " +
|
||||
"and ask them whether the key they see in their User Settings " +
|
||||
"for this device matches the key below:") }
|
||||
</p>
|
||||
<div className="mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>{ _t("Device name") }:</label> <span>{ this.props.device.getDisplayName() }</span></li>
|
||||
<li><label>{ _t("Device ID") }:</label> <span><code>{ this.props.device.deviceId }</code></span></li>
|
||||
<li><label>{ _t("Device key") }:</label> <span><code><b>{ key }</b></code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
{ _t("If it matches, press the verify button below. " +
|
||||
"If it doesn't, then someone else is intercepting this device " +
|
||||
"and you probably want to press the blacklist button instead.") }
|
||||
</p>
|
||||
<p>
|
||||
{ _t("In future this verification process will be more sophisticated.") }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title={_t("Verify device")}
|
||||
description={body}
|
||||
button={_t("I verify that the keys match")}
|
||||
onFinished={this._onLegacyFinished}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.mode === MODE_LEGACY) {
|
||||
return this._renderLegacyVerification();
|
||||
} else {
|
||||
return <div>
|
||||
{this._renderSasVerification()}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeviceVerifyDialog.propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
218
src/components/views/dialogs/IncomingSasDialog.js
Normal file
218
src/components/views/dialogs/IncomingSasDialog.js
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {verificationMethods} from 'matrix-js-sdk/lib/crypto';
|
||||
|
||||
const PHASE_START = 0;
|
||||
const PHASE_SHOW_SAS = 1;
|
||||
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 2;
|
||||
const PHASE_VERIFIED = 3;
|
||||
const PHASE_CANCELLED = 4;
|
||||
|
||||
export default class IncomingSasDialog extends React.Component {
|
||||
static propTypes = {
|
||||
verifier: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._showSasEvent = null;
|
||||
this.state = {
|
||||
phase: PHASE_START,
|
||||
};
|
||||
this.props.verifier.on('show_sas', this._onVerifierShowSas);
|
||||
this.props.verifier.on('cancel', this._onVerifierCancel);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.phase !== PHASE_CANCELLED && this.state.phase !== PHASE_VERIFIED) {
|
||||
this.props.verifier.cancel('User cancel');
|
||||
}
|
||||
this.props.verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
}
|
||||
|
||||
_onFinished = () => {
|
||||
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
|
||||
}
|
||||
|
||||
_onCancelClick = () => {
|
||||
this.props.onFinished(this.state.phase === PHASE_VERIFIED);
|
||||
}
|
||||
|
||||
_onContinueClick = () => {
|
||||
this.setState({phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM});
|
||||
this.props.verifier.verify().then(() => {
|
||||
this.setState({phase: PHASE_VERIFIED});
|
||||
}).catch((e) => {
|
||||
console.log("Verification failed", e);
|
||||
});
|
||||
}
|
||||
|
||||
_onVerifierShowSas = (e) => {
|
||||
this._showSasEvent = e;
|
||||
this.setState({
|
||||
phase: PHASE_SHOW_SAS,
|
||||
sas: e.sas,
|
||||
});
|
||||
}
|
||||
|
||||
_onVerifierCancel = (e) => {
|
||||
this.setState({
|
||||
phase: PHASE_CANCELLED,
|
||||
});
|
||||
}
|
||||
|
||||
_onSasMatchesClick = () => {
|
||||
this._showSasEvent.confirm();
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
|
||||
});
|
||||
}
|
||||
|
||||
_onVerifiedDoneClick = () => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
|
||||
_renderPhaseStart() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{this.props.verifier.userId}</h2>
|
||||
<p>{_t(
|
||||
"Verify this user to mark them as trusted. " +
|
||||
"Trusting users gives you extra peace of mind when using " +
|
||||
"end-to-end encrypted messages."
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
// NB. Below wording adjusted to singular 'device' until we have
|
||||
// cross-signing
|
||||
"Verifying this user will mark their device as trusted, and " +
|
||||
"also mark your device as trusted to them"
|
||||
)}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Continue')}
|
||||
hasCancel={true}
|
||||
onPrimaryButtonClick={this._onContinueClick}
|
||||
onCancel={this._onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPhaseShowSas() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t(
|
||||
"Verify this user by confirming the following number appears on their screen"
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"For maximum security, we reccommend you do this in person or use another " +
|
||||
"trusted means of communication"
|
||||
)}</p>
|
||||
<pre>{this._showSasEvent.sas}</pre>
|
||||
<DialogButtons onPrimaryButtonClick={this._onSasMatchesClick}
|
||||
primaryButton={_t("This Matches")}
|
||||
hasCancel={true}
|
||||
onCancel={this._onCancelClick}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderPhaseWaitForPartnerToConfirm() {
|
||||
const Spinner = sdk.getComponent("views.elements.Spinner");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
<p>{_t("Waiting for partner to confirm...")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPhaseVerified() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{_t(
|
||||
"Verification Complete!"
|
||||
)}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Done')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this._onVerifiedDoneClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPhaseCancelled() {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{_t(
|
||||
"The other party cancelled the verification."
|
||||
)}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Cancel')}
|
||||
hasCancel={false}
|
||||
onPrimaryButtonClick={this._onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("rendering pahse "+this.state.phase);
|
||||
let body;
|
||||
switch (this.state.phase) {
|
||||
case PHASE_START:
|
||||
body = this._renderPhaseStart();
|
||||
break;
|
||||
case PHASE_SHOW_SAS:
|
||||
body = this._renderPhaseShowSas();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
|
||||
body = this._renderPhaseWaitForPartnerToConfirm();
|
||||
break;
|
||||
case PHASE_VERIFIED:
|
||||
body = this._renderPhaseVerified();
|
||||
break;
|
||||
case PHASE_CANCELLED:
|
||||
body = this._renderPhaseCancelled();
|
||||
break;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("Incoming Verification Request")}
|
||||
onFinished={this._onFinished}
|
||||
>
|
||||
{body}
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +114,8 @@
|
|||
"Failed to invite": "Failed to invite",
|
||||
"Failed to invite users to the room:": "Failed to invite users to the room:",
|
||||
"Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:",
|
||||
"Waiting for %(userId)s to accept...": "Waiting for %(userId)s to accept...",
|
||||
"Waiting for %(userId)s to confirm...": "Waiting for %(userId)s to confirm...",
|
||||
"You need to be logged in.": "You need to be logged in.",
|
||||
"You need to be able to invite users to do that.": "You need to be able to invite users to do that.",
|
||||
"Unable to create widget.": "Unable to create widget.",
|
||||
|
@ -264,6 +266,7 @@
|
|||
"Backup of encryption keys to server": "Backup of encryption keys to server",
|
||||
"Render simple counters in room header": "Render simple counters in room header",
|
||||
"Allow up to 6 rooms in a community to be shown simultaneously in a grid via the context menu": "Allow up to 6 rooms in a community to be shown simultaneously in a grid via the context menu",
|
||||
"Two-way device verification using short text": "Two-way device verification using short text",
|
||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||
"Use compact timeline layout": "Use compact timeline layout",
|
||||
"Hide removed messages": "Hide removed messages",
|
||||
|
@ -938,12 +941,22 @@
|
|||
"Please forget all messages I have sent when my account is deactivated (<b>Warning:</b> this will cause future users to see an incomplete view of conversations)": "Please forget all messages I have sent when my account is deactivated (<b>Warning:</b> this will cause future users to see an incomplete view of conversations)",
|
||||
"To continue, please enter your password:": "To continue, please enter your password:",
|
||||
"password": "password",
|
||||
"Verify device": "Verify device",
|
||||
"Use Legacy Verification (for older clients)": "Use Legacy Verification (for older clients)",
|
||||
"Do clicky clicky button press request verify user send to do.": "Do clicky clicky button press request verify user send to do.",
|
||||
"Send Verification Request": "Send Verification Request",
|
||||
"Verify this user by confirming the following number appears on their screen": "Verify this user by confirming the following number appears on their screen",
|
||||
"For maximum security, we reccommend you do this in person or use another trusted means of communication": "For maximum security, we reccommend you do this in person or use another trusted means of communication",
|
||||
"This Matches": "This Matches",
|
||||
"Verification complete!": "Verification complete!",
|
||||
"Done": "Done",
|
||||
"%(userId)s cancelled the verification.": "%(userId)s cancelled the verification.",
|
||||
"Use two-way text verification": "Use two-way text verification",
|
||||
"To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:",
|
||||
"Device name": "Device name",
|
||||
"Device key": "Device key",
|
||||
"If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.",
|
||||
"In future this verification process will be more sophisticated.": "In future this verification process will be more sophisticated.",
|
||||
"Verify device": "Verify device",
|
||||
"I verify that the keys match": "I verify that the keys match",
|
||||
"Back": "Back",
|
||||
"Send Custom Event": "Send Custom Event",
|
||||
|
@ -960,6 +973,12 @@
|
|||
"Toolbox": "Toolbox",
|
||||
"Developer Tools": "Developer Tools",
|
||||
"An error has occurred.": "An error has occurred.",
|
||||
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
|
||||
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them",
|
||||
"Waiting for partner to confirm...": "Waiting for partner to confirm...",
|
||||
"Verification Complete!": "Verification Complete!",
|
||||
"The other party cancelled the verification.": "The other party cancelled the verification.",
|
||||
"Incoming Verification Request": "Incoming Verification Request",
|
||||
"You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.",
|
||||
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.",
|
||||
"Start verification": "Start verification",
|
||||
|
|
|
@ -114,6 +114,12 @@ export const SETTINGS = {
|
|||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"feature_sas": {
|
||||
isFeature: true,
|
||||
displayName: _td("Two-way device verification using short text"),
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
},
|
||||
"MessageComposerInput.dontSuggestEmoji": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td('Disable Emoji suggestions while typing'),
|
||||
|
|
Loading…
Reference in a new issue