Refactored converse-otr.js

- Removed password encryption of the key.
  It never properly worked and bloated the build through extra dependencies.
- Store the key and instance tag on the ChatBox model.
This commit is contained in:
JC Brand 2017-02-15 10:20:57 +00:00
parent fd97edf4f5
commit ba9d3c36d5
5 changed files with 35 additions and 125 deletions

View File

@ -6,7 +6,6 @@
"devDependencies": {
"bootstrap": "~3.2.0",
"bourbon": "~4.2.6",
"crypto-js-evanvosberg": "https://github.com/evanvosberg/crypto-js.git#release-3.1.2-5",
"fontawesome": "~4.1.0"
},
"dependencies": {},

View File

@ -67,21 +67,8 @@ require.config({
"converse-vcard": "src/converse-vcard",
// Off-the-record-encryption
"bigint": "node_modules/otr/vendor/bigint",
"bigint": "node_modules/otr/build/dep/bigint",
"crypto": "node_modules/otr/build/dep/crypto",
"aes": "node_modules/crypto-js/aes",
"cipher-core": "node_modules/crypto-js/cipher-core",
"core": "node_modules/crypto-js/core",
"const": "node_modules/otr/lib/const",
"helpers": "node_modules/otr/lib/helpers",
"sha1": "node_modules/crypto-js/sha1",
"hmac": "node_modules/crypto-js/hmac",
"enc-base64": "node_modules/crypto-js/enc-base64",
"evpkdf": "node_modules/crypto-js/evpkdf",
"md5": "node_modules/crypto-js/md5",
"mode-ctr": "node_modules/crypto-js/mode-ctr",
"pad-nopadding": "node_modules/crypto-js/pad-nopadding",
"sha256": "node_modules/crypto-js/sha256",
"salsa20": "node_modules/otr/build/dep/salsa20",
"otr": "node_modules/otr/build/otr",

View File

@ -39,7 +39,6 @@
"backbone.overview": "0.0.3",
"bower": "latest",
"clean-css": "^3.4.19",
"crypto-js": "3.1.2-5",
"eslint": "^3.14.1",
"eslint-plugin-lodash": "^2.3.3",
"greenkeeper": "^4.1.0",

View File

@ -7,31 +7,6 @@
return describe("The OTR module", function() {
it("can store a session passphrase in session storage", mock.initConverse(function (_converse) {
// With no prebind, the user's XMPP password is used and nothing is
// stored in session storage.
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.createContacts(_converse, 'current');
var auth = _converse.authentication;
var pass = _converse.connection.pass;
_converse.authentication = "manual";
_converse.connection.pass = 's3cr3t!';
expect(_converse.otr.getSessionPassphrase()).toBe(_converse.connection.pass);
// With prebind, a random passphrase is generated and stored in
// session storage.
_converse.authentication = "prebind";
var pp = _converse.otr.getSessionPassphrase();
expect(pp).not.toBe(_converse.connection.pass);
expect(pp).toBe(window.sessionStorage[b64_sha1(_converse.connection.jid)]);
// Clean up
_converse.authentication = auth;
_converse.connection.pass = pass;
}));
it("will add processing hints to sent out encrypted <message> stanzas", mock.initConverse(function (_converse) {
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);

View File

@ -13,11 +13,9 @@
define(["converse-chatview",
"tpl!toolbar_otr",
'otr',
'crypto',
'aes'
'otr'
], factory);
}(this, function (converse, tpl_toolbar_otr, otr, CryptoJS) {
}(this, function (converse, tpl_toolbar_otr, otr) {
"use strict";
// Strophe methods for building stanzas
@ -33,7 +31,6 @@
));
var HAS_CRYPTO = HAS_CSPRNG && (
(!_.isUndefined(CryptoJS)) &&
(!_.isUndefined(otr.OTR)) &&
(!_.isUndefined(otr.DSA))
);
@ -59,14 +56,8 @@
//
// New functions which don't exist yet can also be added.
_initialize: function () {
this.__super__._initialize.apply(this, arguments);
this.otr = new this.OTR();
},
registerGlobalEventHandlers: function () {
this.__super__.registerGlobalEventHandlers();
$(document).click(function () {
if ($('.toggle-otr ul').is(':visible')) {
$('.toggle-otr ul', this).slideUp();
@ -81,9 +72,7 @@
initialize: function () {
this.__super__.initialize.apply(this, arguments);
if (this.get('box_id') !== 'controlbox') {
this.save({
'otr_status': this.get('otr_status') || UNENCRYPTED
});
this.save({'otr_status': this.get('otr_status') || UNENCRYPTED});
}
},
@ -123,31 +112,34 @@
}
}
},
generatePrivateKey: function (instance_tag) {
var _converse = this.__super__._converse;
var key = new otr.DSA();
var jid = _converse.connection.jid;
if (_converse.cache_otr_key) {
this.save({
'otr_priv_key': key.packPrivate(),
'otr_instance_tag': instance_tag
});
}
return key;
},
getSession: function (callback) {
var _converse = this.__super__._converse,
__ = _converse.__;
var cipher = CryptoJS.lib.PasswordBasedCipher;
var pass, instance_tag, saved_key, pass_check;
var instance_tag, saved_key;
if (_converse.cache_otr_key) {
pass = _converse.otr.getSessionPassphrase();
if (!_.isUndefined(pass)) {
instance_tag = window.sessionStorage[b64_sha1(this.id+'instance_tag')];
saved_key = window.sessionStorage[b64_sha1(this.id+'priv_key')];
pass_check = window.sessionStorage[b64_sha1(this.connection.jid+'pass_check')];
if (saved_key && instance_tag && !_.isUndefined(pass_check)) {
var decrypted = cipher.decrypt(CryptoJS.algo.AES, saved_key, pass);
var key = otr.DSA.parsePrivate(decrypted.toString(CryptoJS.enc.Latin1));
if (cipher.decrypt(CryptoJS.algo.AES, pass_check, pass).toString(CryptoJS.enc.Latin1) === 'match') {
// Verified that the passphrase is still the same
this.trigger('showHelpMessages', [__('Re-establishing encrypted session')]);
callback({
'key': key,
'instance_tag': instance_tag
});
return; // Our work is done here
}
}
instance_tag = this.get('otr_instance_tag');
saved_key = otr.DSA.parsePrivate(this.get('otr_priv_key'));
if (saved_key && instance_tag) {
this.trigger('showHelpMessages', [__('Re-establishing encrypted session')]);
callback({
'key': saved_key,
'instance_tag': instance_tag
});
return; // Our work is done here
}
}
// We need to generate a new key and instance tag
@ -157,10 +149,11 @@
null,
true // show spinner
);
var that = this;
window.setTimeout(function () {
var instance_tag = otr.OTR.makeInstanceTag();
callback({
'key': _converse.otr.generatePrivateKey.call(this, instance_tag),
'key': that.generatePrivateKey(instance_tag),
'instance_tag': instance_tag
});
}, 500);
@ -466,6 +459,12 @@
var _converse = this._converse,
__ = _converse.__;
this.updateSettings({
allow_otr: true,
cache_otr_key: false,
use_otr_by_default: false
});
// Add new HTML template
_converse.templates.toolbar_otr = tpl_toolbar_otr;
@ -480,59 +479,10 @@
OTR_TRANSLATED_MAPPING[VERIFIED] = __('verified');
OTR_TRANSLATED_MAPPING[FINISHED] = __('finished');
// For translations
__ = utils.__.bind(_converse);
// Configuration values for this plugin
var settings = {
allow_otr: true,
cache_otr_key: false,
use_otr_by_default: false
};
_.extend(_converse.default_settings, settings);
_.extend(_converse, settings);
_.extend(_converse, _.pick(_converse.user_settings, _.keys(settings)));
// Only allow OTR if we have the capability
_converse.allow_otr = _converse.allow_otr && HAS_CRYPTO;
// Only use OTR by default if allow OTR is enabled to begin with
_converse.use_otr_by_default = _converse.use_otr_by_default && _converse.allow_otr;
// Backbone Models and Views
// -------------------------
_converse.OTR = Backbone.Model.extend({
// A model for managing OTR settings.
getSessionPassphrase: function () {
if (_converse.authentication === 'prebind') {
var key = b64_sha1(_converse.connection.jid),
pass = window.sessionStorage[key];
if (_.isUndefined(pass)) {
pass = Math.floor(Math.random()*4294967295).toString();
window.sessionStorage[key] = pass;
}
return pass;
} else {
return _converse.connection.pass;
}
},
generatePrivateKey: function (instance_tag) {
var key = new otr.DSA();
var jid = _converse.connection.jid;
if (_converse.cache_otr_key) {
var cipher = CryptoJS.lib.PasswordBasedCipher;
var pass = this.getSessionPassphrase();
if (!_.isUndefined(pass)) {
// Encrypt the key and set in sessionStorage. Also store instance tag.
window.sessionStorage[b64_sha1(jid+'priv_key')] =
cipher.encrypt(CryptoJS.algo.AES, key.packPrivate(), pass).toString();
window.sessionStorage[b64_sha1(jid+'instance_tag')] = instance_tag;
window.sessionStorage[b64_sha1(jid+'pass_check')] =
cipher.encrypt(CryptoJS.algo.AES, 'match', pass).toString();
}
}
return key;
}
});
}
});
}));