New build

This commit is contained in:
JC Brand 2018-08-27 16:46:52 +02:00
parent 1dc1c1f98a
commit da68ea9c9f
2 changed files with 107 additions and 50 deletions

View File

@ -8578,6 +8578,7 @@ body.reset {
#conversejs #converse-modals #user-profile-modal .profile-form label {
font-weight: bold; }
#conversejs #converse-modals #user-profile-modal .fingerprint-removal label {
display: flex;
padding: 0.75rem 1.25rem; }
#conversejs #converse-modals #user-profile-modal .list-group-item {
display: flex;
@ -8592,6 +8593,8 @@ body.reset {
display: flex;
justify-content: space-between;
font-size: 95%; }
#conversejs #converse-modals .fingerprint-trust .fingerprint {
margin-left: 1em; }
#conversejs #converse-roster {
text-align: left;

154
dist/converse.js vendored
View File

@ -62863,8 +62863,17 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
const msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
message = msgid && this.messages.findWhere({
msgid
}),
older_versions = message.get('older_versions') || [];
});
if (!message) {
// XXX: Looks like we received a correction for a
// non-existing message, probably due to MAM.
// Not clear what can be done about this... we'll
// just create it as a separate message for now.
return false;
}
const older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': _converse.chatboxes.getMessageBody(stanza),
@ -63918,7 +63927,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'_converse': _converse,
'allow_contact_removal': _converse.allow_contact_removal,
'display_name': this.model.getDisplayName(),
'is_roster_contact': !_.isUndefined(this.model.contact)
'is_roster_contact': !_.isUndefined(this.model.contact),
'utils': u
}));
},
@ -74090,7 +74100,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
});
return {
'identity_key': bundle_el.querySelector('identityKey').textContent,
'identity_key': bundle_el.querySelector('identityKey').textContent.trim(),
'signed_prekey': {
'id': parseInt(signed_prekey_public_el.getAttribute('signedPreKeyId'), 10),
'public_key': signed_prekey_public_el.textContent,
@ -74281,34 +74291,35 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
decrypt(attrs) {
const _converse = this.__super__._converse,
devicelist = _converse.devicelists.get(attrs.from),
device = devicelist.devices.get(attrs.encrypted.device_id),
address = new libsignal.SignalProtocolAddress(attrs.from, parseInt(attrs.encrypted.device_id, 10)),
session_cipher = new window.libsignal.SessionCipher(_converse.omemo_store, address),
libsignal_payload = JSON.parse(atob(attrs.encrypted.key));
libsignal_payload = JSON.parse(atob(attrs.encrypted.key)); // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
if (attrs.encrypted.prekey === 'true') {
// If this is the case, a new session is built from this received element. The client
// SHOULD then republish their bundle information, replacing the used PreKey, such
// that it won't be used again by a different client. If the client already has a session
// with the sender's device, it MUST replace this session with the newly built session.
// The client MUST delete the private key belonging to the PreKey after use.
let plaintext;
return session_cipher.decryptPreKeyWhisperMessage(libsignal_payload.body, 'binary').then(key_and_tag => {
const aes_data = this.getKeyAndTag(u.arrayBufferToString(key_and_tag));
return this.decryptMessage(_.extend(attrs.encrypted, {
'key': aes_data.key,
'tag': aes_data.tag
}));
}).then(plaintext => {
// TODO the prekey should now have been removed.
// Double-check that this is the case and then
// generate a new key to replace it, before
// republishing.
_converse.omemo_store.publishBundle();
if (attrs.encrypted.payload) {
const aes_data = this.getKeyAndTag(u.arrayBufferToString(key_and_tag));
return this.decryptMessage(_.extend(attrs.encrypted, {
'key': aes_data.key,
'tag': aes_data.tag
}));
}
return _.extend(attrs, {
'plaintext': plaintext
});
return Promise.resolve();
}).then(pt => {
plaintext = pt;
return _converse.omemo_store.generateMissingPreKeys();
}).then(() => _converse.omemo_store.publishBundle()).then(() => {
if (plaintext) {
return _.extend(attrs, {
'plaintext': plaintext
});
} else {
return _.extend(attrs, {
'is_only_key': true
});
}
}).catch(e => {
this.reportDecryptionError(e);
return attrs;
@ -74466,7 +74477,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
// concatenation is encrypted using the corresponding
// long-standing SignalProtocol session.
const promises = devices.filter(device => device.get('trusted') != UNTRUSTED).map(device => this.encryptKey(obj.key_and_tag, device));
return Promise.all(promises).then(dicts => this.addKeysToMessageStanza(stanza, dicts, obj.iv)).then(stanza => stanza.c('payload').t(obj.payload));
return Promise.all(promises).then(dicts => this.addKeysToMessageStanza(stanza, dicts, obj.iv)).then(stanza => {
stanza.c('payload').t(obj.payload).up().up();
stanza.c('store', {
'xmlns': Strophe.NS.HINTS
});
return stanza;
});
});
},
@ -74496,6 +74513,13 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'click .toggle-omemo': 'toggleOMEMO'
},
showMessage(message) {
// We don't show a message if it's only keying material
if (!message.get('is_only_key')) {
return this.__super__.showMessage.apply(this, arguments);
}
},
renderOMEMOToolbarButton() {
const _converse = this.__super__._converse,
__ = _converse.__;
@ -74538,12 +74562,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
function generateFingerprint(device) {
let bundle;
return device.getBundle().then(b => {
bundle = b;
return crypto.subtle.digest('SHA-1', u.base64ToArrayBuffer(bundle['identity_key']));
}).then(fp => {
bundle['fingerprint'] = u.arrayBufferToHex(fp);
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
});
@ -74553,6 +74577,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return _converse.getDevicesForContact(jid).then(devices => Promise.all(devices.map(d => generateFingerprint(d))));
};
_converse.getDeviceForContact = function (jid, device_id) {
return _converse.getDevicesForContact(jid).then(devices => devices.get(device_id));
};
_converse.getDevicesForContact = function (jid) {
let devicelist;
return _converse.api.waitUntil('OMEMOInitialized').then(() => {
@ -74778,6 +74806,32 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return _converse.api.sendIQ(stanza);
},
generateMissingPreKeys() {
const current_keys = this.getPreKeys(),
missing_keys = _.difference(_.invokeMap(_.range(0, _converse.NUM_PREKEYS), Number.prototype.toString), _.keys(current_keys));
if (missing_keys.length < 1) {
_converse.log("No missing prekeys to generate for our own device", Strophe.LogLevel.WARN);
return Promise.resolve();
}
return Promise.all(_.map(missing_keys, id => libsignal.KeyHelper.generatePreKey(parseInt(id, 10)))).then(keys => {
_.forEach(keys, k => this.storePreKey(k.keyId, k.keyPair));
const marshalled_keys = _.map(this.getPreKeys(), k => ({
'id': k.keyId,
'key': u.arrayBufferToBase64(k.pubKey)
})),
devicelist = _converse.devicelists.get(_converse.bare_jid),
device = devicelist.devices.get(this.get('device_id'));
return device.getBundle().then(bundle => device.save('bundle', _.extend(bundle, {
'prekeys': marshalled_keys
})));
});
},
generateBundle() {
/* The first thing that needs to happen if a client wants to
* start using OMEMO is they need to generate an IdentityKey
@ -75359,6 +75413,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
'label_role': __('Role'),
'label_role_help': __('Use commas to separate multiple roles. Your roles are shown next to your name on your chat messages.'),
'label_url': __('URL'),
'utils': u,
'view': this
}));
},
@ -81638,8 +81693,8 @@ __p += '\n <div class="tab-pane fade" id="omemo-tabpanel"
__e(o.__("This device's OMEMO fingerprint")) +
'</li>\n <li class="list-group-item">\n ';
if (o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ;
__p += '\n <span class="fingerprint">' +
__e(o.view.current_device.get('bundle').fingerprint) +
__p += '\n <span class="fingerprint">' +
__e(o.utils.formatFingerprint(o.view.current_device.get('bundle').fingerprint)) +
'</span>\n ';
} else {;
__p += '\n <span class="spinner fa fa-spinner centered"/>\n ';
@ -81661,7 +81716,7 @@ __e(device.get('id')) +
'"\n aria-label="' +
__e(o.__('Checkbox for selecting the following fingerprint')) +
'">\n <span class="fingerprint">' +
__e(device.get('bundle').fingerprint) +
__e(o.utils.formatFingerprint(device.get('bundle').fingerprint)) +
'</span>\n </label>\n </li>\n ';
} else {;
__p += '\n <li class="fingerprint-removal-item list-group-item nopadding">\n <label>\n <input type="checkbox" value="' +
@ -82727,7 +82782,7 @@ __p += ' checked="checked" ';
__p += '>' +
__e(o.__('Untrusted')) +
'\n </label>\n </div>\n <span class="fingerprint">' +
__e(device.get('bundle').fingerprint) +
__e(o.utils.formatFingerprint(device.get('bundle').fingerprint)) +
'</span>\n </form>\n </li>\n ';
} ;
__p += '\n ';
@ -83711,22 +83766,21 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return result;
};
u.arrayBufferToHex = function (ab) {
const hexCodes = [];
const padding = '00000000';
const view = new window.DataView(ab);
u.formatFingerprint = function (fp) {
fp = fp.replace(/^05/, '');
const arr = [];
for (var i = 0; i < view.byteLength; i += 4) {
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
const value = view.getUint32(i); // toString(16) will give the hex representation of the number without padding
const stringValue = value.toString(16); // We use concatenation and slice for padding
const paddedValue = (padding + stringValue).slice(-padding.length);
hexCodes.push(paddedValue);
for (let i = 1; i < 8; i++) {
const idx = i * 8 + i - 1;
fp = fp.slice(0, idx) + ' ' + fp.slice(idx);
}
return hexCodes.join("");
return fp;
};
u.arrayBufferToHex = function (ab) {
// https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex#40031979
return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
};
u.arrayBufferToString = function (ab) {