signal-desktop/js/views/key_verification_view.js

147 lines
4.2 KiB
JavaScript
Raw Normal View History

/* global Whisper, textsecure, QRCode, dcodeIO, libsignal, i18n, _ */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
2018-04-27 21:25:04 +00:00
(function() {
'use strict';
2018-04-27 21:25:04 +00:00
window.Whisper = window.Whisper || {};
2018-04-27 21:25:04 +00:00
Whisper.KeyVerificationPanelView = Whisper.View.extend({
className: 'key-verification panel',
templateName: 'key-verification',
events: {
'click button.verify': 'toggleVerified',
},
initialize(options) {
2018-04-27 21:25:04 +00:00
this.ourNumber = textsecure.storage.user.getNumber();
if (options.newKey) {
this.theirKey = options.newKey;
}
this.loadTheirKey();
this.loadOurKey();
this.render();
if (options.onLoad) {
options.onLoad(this);
}
this.loadKeys().then(() => {
this.listenTo(this.model, 'change', this.render);
});
2018-04-27 21:25:04 +00:00
},
async loadKeys() {
await this.generateSecurityNumber();
this.render();
2018-04-27 21:25:04 +00:00
},
makeQRCode() {
2018-04-27 21:25:04 +00:00
// Per Lilia: We can't turn this on until it generates a Latin1 string, as is
// required by the mobile clients.
new QRCode(this.$('.qr')[0]).makeCode(
dcodeIO.ByteBuffer.wrap(this.ourKey).toString('base64')
);
},
loadTheirKey() {
const item = textsecure.storage.protocol.identityKeys[this.model.id];
this.theirKey = item ? item.publicKey : null;
2018-04-27 21:25:04 +00:00
},
loadOurKey() {
const item = textsecure.storage.protocol.identityKeys[this.ourNumber];
this.ourKey = item ? item.publicKey : null;
2018-04-27 21:25:04 +00:00
},
generateSecurityNumber() {
2018-04-27 21:25:04 +00:00
return new libsignal.FingerprintGenerator(5200)
.createFor(this.ourNumber, this.ourKey, this.model.id, this.theirKey)
.then(securityNumber => {
this.securityNumber = securityNumber;
});
2018-04-27 21:25:04 +00:00
},
onSafetyNumberChanged() {
2018-04-27 21:25:04 +00:00
this.model.getProfiles().then(this.loadKeys.bind(this));
const dialog = new Whisper.ConfirmationDialogView({
2018-04-27 21:25:04 +00:00
message: i18n('changedRightAfterVerify', [
this.model.getTitle(),
this.model.getTitle(),
]),
hideCancel: true,
});
2018-04-27 21:25:04 +00:00
dialog.$el.insertBefore(this.el);
dialog.focusCancel();
},
toggleVerified() {
2018-04-27 21:25:04 +00:00
this.$('button.verify').attr('disabled', true);
this.model
.toggleVerified()
.catch(result => {
if (result instanceof Error) {
if (result.name === 'OutgoingIdentityKeyError') {
this.onSafetyNumberChanged();
2018-04-27 21:25:04 +00:00
} else {
window.log.error(
'failed to toggle verified:',
result && result.stack ? result.stack : result
);
}
} else {
const keyError = _.some(
result.errors,
error => error.name === 'OutgoingIdentityKeyError'
);
if (keyError) {
this.onSafetyNumberChanged();
} else {
_.forEach(result.errors, error => {
window.log.error(
'failed to toggle verified:',
error && error.stack ? error.stack : error
);
2018-04-27 21:25:04 +00:00
});
}
}
})
.then(() => {
this.$('button.verify').removeAttr('disabled');
});
2018-04-27 21:25:04 +00:00
},
render_attributes() {
const s = this.securityNumber;
const chunks = [];
if (s) {
for (let i = 0; i < s.length; i += 5) {
chunks.push(s.substring(i, i + 5));
}
} else {
for (let i = 0; i < 12; i += 1) {
chunks.push('XXXXX');
}
2018-04-27 21:25:04 +00:00
}
const name = this.model.getTitle();
const yourSafetyNumberWith = i18n(
'yourSafetyNumberWith',
this.model.getTitle()
);
const isVerified = this.model.isVerified();
const verifyButton = isVerified ? i18n('unverify') : i18n('verify');
const verifiedStatus = isVerified
2018-04-27 21:25:04 +00:00
? i18n('isVerified', name)
: i18n('isNotVerified', name);
2018-04-27 21:25:04 +00:00
return {
learnMore: i18n('learnMore'),
theirKeyUnknown: i18n('theirIdentityUnknown'),
yourSafetyNumberWith,
2018-04-27 21:25:04 +00:00
verifyHelp: i18n('verifyHelp', this.model.getTitle()),
verifyButton,
2018-04-27 21:25:04 +00:00
hasTheirKey: this.theirKey !== undefined,
chunks,
isVerified,
verifiedStatus,
2018-04-27 21:25:04 +00:00
};
},
});
})();