Commit 3b60ced3 authored by JC Brand's avatar JC Brand

Add a button for regenerating your OMEMO device bundle

parent c2e549e7
......@@ -4,6 +4,7 @@
- Error `FATAL: TypeError: Cannot read property 'extend' of undefined` when using `embedded` view mode.
- Default paths in converse-notifications.js are now relative
- Add a button to regenerate OMEMO keys
- #1188 Feature request: drag and drop file to HTTP Upload
- #1268 Switch from SASS variables to CSS custom properties
- #1278 Replace the default avatar with a SVG version
......
......@@ -65359,6 +65359,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
ProfileModal: {
events: {
'change input.select-all': 'selectAll',
'click .generate-bundle': 'generateOMEMODeviceBundle',
'submit .fingerprint-removal': 'removeSelectedFingerprints'
},
......@@ -65368,6 +65369,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
this.devicelist = _converse.devicelists.get(_converse.bare_jid);
this.devicelist.devices.on('change:bundle', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
this.devicelist.devices.on('remove', this.debouncedRender, this);
this.devicelist.devices.on('add', this.debouncedRender, this);
return this.__super__.initialize.apply(this, arguments);
......@@ -65377,7 +65379,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
const _converse = this.__super__._converse,
device_id = _converse.omemo_store.get('device_id');
this.current_device = this.devicelist.devices.get(device_id);
if (device_id) {
this.current_device = this.devicelist.devices.get(device_id);
}
this.other_devices = this.devicelist.devices.filter(d => d.get('id') !== device_id);
if (this.__super__.beforeRender) {
......@@ -65410,6 +65415,17 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
_converse.api.alert.show(Strophe.LogLevel.ERROR, __('Error'), [__('Sorry, an error occurred while trying to remove the devices.')]);
});
},
generateOMEMODeviceBundle(ev) {
const _converse = this.__super__._converse,
__ = _converse.__,
api = _converse.api;
ev.preventDefault();
if (confirm(__("Are you sure you want to generate new OMEMO keys?" + "This will remove your old keys and all previously encrypted messages will no longer be ecryptable on this device."))) {
api.omemo.bundle.generate();
}
}
},
......@@ -65453,27 +65469,26 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
}).then(() => this.buildSessions(devices));
},
buildSession(device) {
async buildSession(device) {
const _converse = this.__super__._converse,
address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id')),
sessionBuilder = new libsignal.SessionBuilder(_converse.omemo_store, address),
prekey = device.getRandomPreKey();
return device.getBundle().then(bundle => {
return sessionBuilder.processPreKey({
'registrationId': parseInt(device.get('id'), 10),
'identityKey': u.base64ToArrayBuffer(bundle.identity_key),
'signedPreKey': {
'keyId': bundle.signed_prekey.id,
// <Number>
'publicKey': u.base64ToArrayBuffer(bundle.signed_prekey.public_key),
'signature': u.base64ToArrayBuffer(bundle.signed_prekey.signature)
},
'preKey': {
'keyId': prekey.id,
// <Number>
'publicKey': u.base64ToArrayBuffer(prekey.key)
}
});
prekey = device.getRandomPreKey(),
bundle = await device.getBundle();
return sessionBuilder.processPreKey({
'registrationId': parseInt(device.get('id'), 10),
'identityKey': u.base64ToArrayBuffer(bundle.identity_key),
'signedPreKey': {
'keyId': bundle.signed_prekey.id,
// <Number>
'publicKey': u.base64ToArrayBuffer(bundle.signed_prekey.public_key),
'signature': u.base64ToArrayBuffer(bundle.signed_prekey.signature)
},
'preKey': {
'keyId': prekey.id,
// <Number>
'publicKey': u.base64ToArrayBuffer(prekey.key)
}
});
},
......@@ -65792,20 +65807,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
function generateFingerprint(device) {
async function generateFingerprint(device) {
if (_.get(device.get('bundle'), 'fingerprint')) {
return;
}
return device.getBundle().then(bundle => {
bundle['fingerprint'] = u.arrayBufferToHex(u.base64ToArrayBuffer(bundle['identity_key']));
device.save('bundle', bundle);
device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
});
const bundle = await device.getBundle();
bundle['fingerprint'] = u.arrayBufferToHex(u.base64ToArrayBuffer(bundle['identity_key']));
device.save('bundle', bundle);
device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
}
_converse.generateFingerprints = function (jid) {
return _converse.getDevicesForContact(jid).then(devices => Promise.all(devices.map(d => generateFingerprint(d))));
_converse.generateFingerprints = async function (jid) {
const devices = await _converse.getDevicesForContact(jid);
return Promise.all(devices.map(d => generateFingerprint(d)));
};
_converse.getDeviceForContact = function (jid, device_id) {
......@@ -66452,6 +66467,61 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
_converse.api.listen.on('profileModalInitialized', contact => {
_converse.generateFingerprints(_converse.bare_jid).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
/************************ BEGIN API ************************/
_.extend(_converse.api, {
/**
* The "omemo" namespace groups methods relevant to OMEMO
* encryption.
*
* @namespace _converse.api.omemo
* @memberOf _converse.api
*/
'omemo': {
/**
* The "bundle" namespace groups methods relevant to the user's
* OMEMO bundle.
*
* @namespace _converse.api.omemo.bundle
* @memberOf _converse.api.omemo
*/
'bundle': {
/**
* Lets you generate a new OMEMO device bundle
*
* @method _converse.api.omemo.bundle.generate
* @returns {promise} Promise which resolves once we have a result from the server.
*/
'generate': async () => {
// Remove current device
const devicelist = _converse.devicelists.get(_converse.bare_jid),
device_id = _converse.omemo_store.get('device_id');
if (device_id) {
const device = devicelist.devices.get(device_id);
_converse.omemo_store.unset(device_id);
if (device) {
await new Promise(done => device.destroy({
'success': done,
'error': done
}));
}
devicelist.devices.trigger('remove');
} // Generate new bundle and publish
await _converse.omemo_store.generateBundle();
await devicelist.publishDevices();
const device = devicelist.devices.get(_converse.omemo_store.get('device_id'));
return generateFingerprint(device);
}
}
}
});
}
});
......@@ -66518,8 +66588,8 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
__ = _converse.__;
_converse.ProfileModal = _converse.BootstrapModal.extend({
events: {
'click .change-avatar': "openFileSelection",
'change input[type="file"': "updateFilePreview",
'click .change-avatar': "openFileSelection",
'submit .profile-form': 'onFormSubmitted'
},
......@@ -102683,7 +102753,9 @@ __e(o.utils.formatFingerprint(o.view.current_device.get('bundle').fingerprint))
} else {;
__p += '\n <span class="spinner fa fa-spinner centered"/>\n ';
} ;
__p += '\n </li>\n </ul>\n ';
__p += '\n </li>\n </ul>\n <div class="form-group">\n <button type="button" class="generate-bundle btn btn-danger">' +
__e(o.__('Generate new keys and fingerprint')) +
'</button>\n </div>\n\n ';
if (o.view.other_devices.length) { ;
__p += '\n <ul class="list-group fingerprints">\n <li class="list-group-item nopadding active">\n <label>\n <input type="checkbox" class="select-all" title="' +
__e(o.__('Select all')) +
......@@ -68,6 +68,7 @@ converse.plugins.add('converse-omemo', {
ProfileModal: {
events: {
'change input.select-all': 'selectAll',
'click .generate-bundle': 'generateOMEMODeviceBundle',
'submit .fingerprint-removal': 'removeSelectedFingerprints'
},
......@@ -77,6 +78,7 @@ converse.plugins.add('converse-omemo', {
this.devicelist = _converse.devicelists.get(_converse.bare_jid);
this.devicelist.devices.on('change:bundle', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
this.devicelist.devices.on('reset', this.debouncedRender, this);
this.devicelist.devices.on('remove', this.debouncedRender, this);
this.devicelist.devices.on('add', this.debouncedRender, this);
return this.__super__.initialize.apply(this, arguments);
......@@ -85,7 +87,10 @@ converse.plugins.add('converse-omemo', {
beforeRender () {
const { _converse } = this.__super__,
device_id = _converse.omemo_store.get('device_id');
this.current_device = this.devicelist.devices.get(device_id);
if (device_id) {
this.current_device = this.devicelist.devices.get(device_id);
}
this.other_devices = this.devicelist.devices.filter(d => (d.get('id') !== device_id));
if (this.__super__.beforeRender) {
return this.__super__.beforeRender.apply(this, arguments);
......@@ -118,6 +123,18 @@ converse.plugins.add('converse-omemo', {
)
});
},
generateOMEMODeviceBundle (ev) {
const { _converse } = this.__super__,
{ __, api } = _converse;
ev.preventDefault();
if (confirm(__(
"Are you sure you want to generate new OMEMO keys?" +
"This will remove your old keys and all previously encrypted messages will no longer be ecryptable on this device.")
)) {
api.omemo.bundle.generate();
}
}
},
UserDetailsModal: {
......@@ -475,20 +492,19 @@ converse.plugins.add('converse-omemo', {
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
function generateFingerprint (device) {
async function generateFingerprint (device) {
if (_.get(device.get('bundle'), 'fingerprint')) {
return;
}
return device.getBundle().then(bundle => {
bundle['fingerprint'] = u.arrayBufferToHex(u.base64ToArrayBuffer(bundle['identity_key']));
device.save('bundle', bundle);
device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
});
const bundle = await device.getBundle();
bundle['fingerprint'] = u.arrayBufferToHex(u.base64ToArrayBuffer(bundle['identity_key']));
device.save('bundle', bundle);
device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
}
_converse.generateFingerprints = function (jid) {
return _converse.getDevicesForContact(jid)
.then(devices => Promise.all(devices.map(d => generateFingerprint(d))))
_converse.generateFingerprints = async function (jid) {
const devices = await _converse.getDevicesForContact(jid)
return Promise.all(devices.map(d => generateFingerprint(d)));
}
_converse.getDeviceForContact = function (jid, device_id) {
......@@ -1044,6 +1060,52 @@ converse.plugins.add('converse-omemo', {
_converse.api.listen.on('profileModalInitialized', (contact) => {
_converse.generateFingerprints(_converse.bare_jid).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
/************************ BEGIN API ************************/
_.extend(_converse.api, {
/**
* The "omemo" namespace groups methods relevant to OMEMO
* encryption.
*
* @namespace _converse.api.omemo
* @memberOf _converse.api
*/
'omemo': {
/**
* The "bundle" namespace groups methods relevant to the user's
* OMEMO bundle.
*
* @namespace _converse.api.omemo.bundle
* @memberOf _converse.api.omemo
*/
'bundle': {
/**
* Lets you generate a new OMEMO device bundle
*
* @method _converse.api.omemo.bundle.generate
* @returns {promise} Promise which resolves once we have a result from the server.
*/
'generate': async () => {
// Remove current device
const devicelist = _converse.devicelists.get(_converse.bare_jid),
device_id = _converse.omemo_store.get('device_id');
if (device_id) {
const device = devicelist.devices.get(device_id);
_converse.omemo_store.unset(device_id);
if (device) {
await new Promise(done => device.destroy({'success': done, 'error': done}));
}
devicelist.devices.trigger('remove');
}
// Generate new bundle and publish
await _converse.omemo_store.generateBundle();
await devicelist.publishDevices();
const device = devicelist.devices.get(_converse.omemo_store.get('device_id'));
return generateFingerprint(device);
}
}
}
});
}
});
......@@ -35,8 +35,8 @@ converse.plugins.add('converse-profile', {
_converse.ProfileModal = _converse.BootstrapModal.extend({
events: {
'click .change-avatar': "openFileSelection",
'change input[type="file"': "updateFilePreview",
'click .change-avatar': "openFileSelection",
'submit .profile-form': 'onFormSubmitted'
},
......
......@@ -78,6 +78,10 @@
{[ } ]}
</li>
</ul>
<div class="form-group">
<button type="button" class="generate-bundle btn btn-danger">{{{o.__('Generate new keys and fingerprint')}}}</button>
</div>
{[ if (o.view.other_devices.length) { ]}
<ul class="list-group fingerprints">
<li class="list-group-item nopadding active">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment