signal-desktop/js/views/key_verification_view.js
2019-08-21 14:52:30 -07:00

146 lines
4.2 KiB
JavaScript

/* global Whisper, textsecure, QRCode, dcodeIO, libsignal, i18n, _ */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.KeyVerificationPanelView = Whisper.View.extend({
className: 'key-verification panel',
templateName: 'key-verification',
events: {
'click button.verify': 'toggleVerified',
},
initialize(options) {
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.loadKeys().then(() => {
this.listenTo(this.model, 'change', this.render);
});
},
async loadKeys() {
await this.generateSecurityNumber();
this.render();
},
makeQRCode() {
// 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;
},
loadOurKey() {
const item = textsecure.storage.protocol.identityKeys[this.ourNumber];
this.ourKey = item ? item.publicKey : null;
},
generateSecurityNumber() {
return new libsignal.FingerprintGenerator(5200)
.createFor(this.ourNumber, this.ourKey, this.model.id, this.theirKey)
.then(securityNumber => {
this.securityNumber = securityNumber;
});
},
onSafetyNumberChanged() {
this.model.getProfiles().then(this.loadKeys.bind(this));
const dialog = new Whisper.ConfirmationDialogView({
message: i18n('changedRightAfterVerify', [
this.model.getTitle(),
this.model.getTitle(),
]),
hideCancel: true,
});
dialog.$el.insertBefore(this.el);
dialog.focusCancel();
},
toggleVerified() {
this.$('button.verify').attr('disabled', true);
this.model
.toggleVerified()
.catch(result => {
if (result instanceof Error) {
if (result.name === 'OutgoingIdentityKeyError') {
this.onSafetyNumberChanged();
} 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
);
});
}
}
})
.then(() => {
this.$('button.verify').removeAttr('disabled');
});
},
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');
}
}
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
? i18n('isVerified', name)
: i18n('isNotVerified', name);
return {
learnMore: i18n('learnMore'),
theirKeyUnknown: i18n('theirIdentityUnknown'),
yourSafetyNumberWith,
verifyHelp: i18n('verifyHelp', this.model.getTitle()),
verifyButton,
hasTheirKey: this.theirKey !== undefined,
chunks,
isVerified,
verifiedStatus,
};
},
});
})();