Bugfix. Updates #111
When using OTR with prebind, the user password isn't defined. ============================================================= When not using prebind, the user password is used to encrypt the private key for the OTR session before it's saved in session storage. When using prebind, we ideally want to use the same OTR private key across page loads, so that we don't have to spend the time generating a new one together with AKE on every page load. To do this, we need to store it somewhere, like the browser's session storage. However, I have yet to find a secure way to store the OTR private key that does not expose it to maliciously injected javascript. For now, I've updated the code to generate a new private key and do the AKE with every page reload. I'm considering adding code to store the private key in Session Storage and letting the user explicitly enable this (while making them aware of the risks involved).
This commit is contained in:
parent
9bce25109c
commit
8232cdaff2
87
converse.js
87
converse.js
@ -451,20 +451,7 @@
|
||||
this.rosterview = new this.RosterView({'model':this.roster});
|
||||
};
|
||||
|
||||
this.onConnected = function () {
|
||||
if (this.debug) {
|
||||
this.connection.xmlInput = function (body) { console.log(body); };
|
||||
this.connection.xmlOutput = function (body) { console.log(body); };
|
||||
Strophe.log = function (level, msg) { console.log(level+' '+msg); };
|
||||
Strophe.error = function (msg) { console.log('ERROR: '+msg); };
|
||||
}
|
||||
this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
||||
this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
||||
this.features = new this.Features();
|
||||
this.initStatus($.proxy(function () {
|
||||
this.initRoster();
|
||||
this.chatboxes.onConnected();
|
||||
this.connection.roster.get(function () {});
|
||||
this.registerGlobalEventHandlers = function () {
|
||||
$(document).click(function() {
|
||||
if ($('.toggle-otr ul').is(':visible')) {
|
||||
$('.toggle-otr ul', this).slideUp();
|
||||
@ -480,6 +467,23 @@
|
||||
}
|
||||
this.windowState = e.type;
|
||||
},this));
|
||||
};
|
||||
|
||||
this.onConnected = function () {
|
||||
if (this.debug) {
|
||||
this.connection.xmlInput = function (body) { console.log(body); };
|
||||
this.connection.xmlOutput = function (body) { console.log(body); };
|
||||
Strophe.log = function (level, msg) { console.log(level+' '+msg); };
|
||||
Strophe.error = function (msg) { console.log('ERROR: '+msg); };
|
||||
}
|
||||
this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
||||
this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
||||
this.features = new this.Features();
|
||||
this.initStatus($.proxy(function () {
|
||||
this.initRoster();
|
||||
this.chatboxes.onConnected();
|
||||
this.connection.roster.get(function () {});
|
||||
this.registerGlobalEventHandlers();
|
||||
this.giveFeedback(__('Online Contacts'));
|
||||
|
||||
if (this.callback) {
|
||||
@ -513,9 +517,6 @@
|
||||
this.ChatBox = Backbone.Model.extend({
|
||||
initialize: function () {
|
||||
if (this.get('box_id') !== 'controlbox') {
|
||||
if (_.contains([UNVERIFIED, VERIFIED], this.get('otr_status'))) {
|
||||
this.initiateOTR();
|
||||
}
|
||||
this.messages = new converse.Messages();
|
||||
this.messages.localStorage = new Backbone.LocalStorage(
|
||||
hex_sha1('converse.messages'+this.get('jid')+converse.bare_jid));
|
||||
@ -527,43 +528,53 @@
|
||||
}
|
||||
},
|
||||
|
||||
getSession: function () {
|
||||
// XXX: sessionStorage is not supported in IE < 8. Perhaps a
|
||||
getSession: function (callback) {
|
||||
// FIXME: sessionStorage is not supported in IE < 8. Perhaps a
|
||||
// user alert is required here...
|
||||
var saved_key = window.sessionStorage[hex_sha1(this.id+'priv_key')];
|
||||
var instance_tag = window.sessionStorage[hex_sha1(this.id+'instance_tag')];
|
||||
var result, pass, instance_tag, saved_key;
|
||||
var cipher = CryptoJS.lib.PasswordBasedCipher;
|
||||
var pass = converse.connection.pass;
|
||||
if (typeof converse.connection.pass !== "undefined") {
|
||||
instance_tag = window.sessionStorage[hex_sha1(this.id+'instance_tag')];
|
||||
saved_key = window.sessionStorage[hex_sha1(this.id+'priv_key')];
|
||||
pass = converse.connection.pass;
|
||||
var pass_check = this.get('pass_check');
|
||||
var result, key;
|
||||
if (saved_key && instance_tag && typeof pass_check !== 'undefined') {
|
||||
var decrypted = cipher.decrypt(CryptoJS.algo.AES, saved_key, pass);
|
||||
key = DSA.parsePrivate(decrypted.toString(CryptoJS.enc.Latin1));
|
||||
var key = DSA.parsePrivate(decrypted.toString(CryptoJS.enc.Latin1));
|
||||
if (cipher.decrypt(CryptoJS.algo.AES, pass_check, pass).toString(CryptoJS.enc.Latin1) === 'match') {
|
||||
// Verified that the user's password is still the same
|
||||
this.trigger('showHelpMessages', [__('Re-establishing encrypted session')]);
|
||||
return {
|
||||
callback({
|
||||
'key': key,
|
||||
'instance_tag': instance_tag
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to generate a new key and instance tag
|
||||
result = alert(__('Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.'));
|
||||
instance_tag = OTR.makeInstanceTag();
|
||||
key = new DSA();
|
||||
this.trigger('showHelpMessages', [__('Generating private key.')]);
|
||||
this.trigger('showHelpMessages', [__('Your browser might become unresponsive.')]);
|
||||
|
||||
var clb = callback;
|
||||
setTimeout($.proxy(function () {
|
||||
var key = new DSA();
|
||||
if (typeof converse.connection.pass !== "undefined") {
|
||||
// Encrypt the key and set in sessionStorage. Also store
|
||||
// instance tag
|
||||
window.sessionStorage[hex_sha1(this.id+'priv_key')] =
|
||||
cipher.encrypt(CryptoJS.algo.AES, key.packPrivate(), pass).toString();
|
||||
window.sessionStorage[hex_sha1(this.id+'instance_tag')] = instance_tag;
|
||||
|
||||
this.trigger('showHelpMessages', [__('Private key generated.')]);
|
||||
this.save({'pass_check': cipher.encrypt(CryptoJS.algo.AES, 'match', pass).toString()});
|
||||
return {
|
||||
}
|
||||
this.trigger('showHelpMessages', [__('Private key generated.')]);
|
||||
clb({
|
||||
'key': key,
|
||||
'instance_tag': instance_tag
|
||||
};
|
||||
});
|
||||
}, this), 500);
|
||||
|
||||
},
|
||||
|
||||
updateOTRStatus: function (state) {
|
||||
@ -616,7 +627,7 @@
|
||||
// query message from our buddy. Otherwise, it is us who will
|
||||
// send the query message to them.
|
||||
this.save({'otr_status': UNENCRYPTED});
|
||||
var session = this.getSession();
|
||||
var session = this.getSession($.proxy(function (session) {
|
||||
this.otr = new OTR({
|
||||
fragment_size: 140,
|
||||
send_interval: 200,
|
||||
@ -637,11 +648,13 @@
|
||||
this.trigger('showOTRError', msg);
|
||||
}, this));
|
||||
|
||||
this.trigger('showHelpMessages', [__('Exchanging private key with buddy.')]);
|
||||
if (query_msg) {
|
||||
this.otr.receiveMsg(query_msg);
|
||||
} else {
|
||||
this.otr.sendQueryMsg();
|
||||
}
|
||||
}, this));
|
||||
},
|
||||
|
||||
endOTR: function () {
|
||||
@ -700,7 +713,11 @@
|
||||
return this.createMessage(message);
|
||||
}
|
||||
if (_.contains([UNVERIFIED, VERIFIED], this.get('otr_status'))) {
|
||||
if (text.match(/^\?OTRv23?/)) {
|
||||
this.initiateOTR(text);
|
||||
} else {
|
||||
this.otr.receiveMsg(text);
|
||||
}
|
||||
} else {
|
||||
if (text.match(/^\?OTR/)) {
|
||||
// They want to initiate OTR
|
||||
@ -849,6 +866,10 @@
|
||||
if (this.model.get('status')) {
|
||||
this.showStatusMessage(this.model.get('status'));
|
||||
}
|
||||
|
||||
if (_.contains([UNVERIFIED, VERIFIED], this.model.get('otr_status'))) {
|
||||
this.model.initiateOTR();
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
|
Loading…
Reference in New Issue
Block a user