some fixes; asynchronous message decryption to keep browser responsive
This commit is contained in:
@@ -995,7 +995,7 @@ module.exports = {
|
||||
|
||||
show_version: true,
|
||||
show_comment: true,
|
||||
versionstring: "OpenPGP.js v1.2.0",
|
||||
versionstring: "OpenPGP.js v1.3.0",
|
||||
commentstring: "http://openpgpjs.org",
|
||||
|
||||
keyserver: "keyserver.linux.it", // "pgp.mit.edu:11371"
|
||||
@@ -5255,7 +5255,6 @@ function ii(a, b, c, d, x, s, t) {
|
||||
}
|
||||
|
||||
function md51(s) {
|
||||
txt = '';
|
||||
var n = s.length,
|
||||
state = [1732584193, -271733879, -1732584194, 271733878],
|
||||
i;
|
||||
@@ -15922,6 +15921,10 @@ S2K.prototype.write = function () {
|
||||
bytes += this.salt;
|
||||
bytes += String.fromCharCode(this.c);
|
||||
break;
|
||||
case 'gnu':
|
||||
throw new Error("GNU s2k type not supported.");
|
||||
default:
|
||||
throw new Error("Unknown s2k type.");
|
||||
}
|
||||
|
||||
return bytes;
|
||||
@@ -15950,8 +15953,8 @@ S2K.prototype.produce_key = function (passphrase, numBytes) {
|
||||
|
||||
case 'iterated':
|
||||
var isp = [],
|
||||
count = s2k.get_count();
|
||||
data = s2k.salt + passphrase;
|
||||
count = s2k.get_count(),
|
||||
data = s2k.salt + passphrase;
|
||||
|
||||
while (isp.length * data.length < count)
|
||||
isp.push(data);
|
||||
@@ -15962,6 +15965,12 @@ S2K.prototype.produce_key = function (passphrase, numBytes) {
|
||||
isp = isp.substr(0, count);
|
||||
|
||||
return crypto.hash.digest(algorithm, prefix + isp);
|
||||
|
||||
case 'gnu':
|
||||
throw new Error("GNU s2k type not supported.");
|
||||
|
||||
default:
|
||||
throw new Error("Unknown s2k type.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16655,8 +16664,8 @@ AsyncProxy.prototype.decryptKey = function(privateKey, password) {
|
||||
});
|
||||
|
||||
self.tasks.push({ resolve:function(data) {
|
||||
var packetlist = packet.List.fromStructuredClone(data);
|
||||
data = new key.Key(packetlist);
|
||||
var packetlist = packet.List.fromStructuredClone(data),
|
||||
data = new key.Key(packetlist);
|
||||
resolve(data);
|
||||
}, reject:reject });
|
||||
});
|
||||
@@ -16683,8 +16692,8 @@ AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password) {
|
||||
});
|
||||
|
||||
self.tasks.push({ resolve:function(data) {
|
||||
var packetlist = packet.List.fromStructuredClone(data);
|
||||
data = new key.Key(packetlist);
|
||||
var packetlist = packet.List.fromStructuredClone(data),
|
||||
data = new key.Key(packetlist);
|
||||
resolve(data);
|
||||
}, reject:reject });
|
||||
});
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
// GPG4Browsers - An OpenPGP implementation in javascript
|
||||
// Copyright (C) 2011 Recurity Labs GmbH
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 3.0 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
window = {}; // to make UMD bundles work
|
||||
|
||||
// Mozilla bind polyfill because phantomjs is stupid
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5 internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
FNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
FNOP.prototype = this.prototype;
|
||||
fBound.prototype = new FNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
importScripts('openpgp.js');
|
||||
|
||||
var MIN_SIZE_RANDOM_BUFFER = 40000;
|
||||
var MAX_SIZE_RANDOM_BUFFER = 60000;
|
||||
|
||||
window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER);
|
||||
|
||||
self.onmessage = function (event) {
|
||||
var data = null,
|
||||
err = null,
|
||||
msg = event.data,
|
||||
correct = false;
|
||||
|
||||
switch (msg.event) {
|
||||
case 'configure':
|
||||
for (var i in msg.config) {
|
||||
window.openpgp.config[i] = msg.config[i];
|
||||
}
|
||||
break;
|
||||
case 'seed-random':
|
||||
if (!(msg.buf instanceof Uint8Array)) {
|
||||
msg.buf = new Uint8Array(msg.buf);
|
||||
}
|
||||
window.openpgp.crypto.random.randomBuffer.set(msg.buf);
|
||||
break;
|
||||
case 'encrypt-message':
|
||||
if (!msg.keys.length) {
|
||||
msg.keys = [msg.keys];
|
||||
}
|
||||
msg.keys = msg.keys.map(packetlistCloneToKey);
|
||||
window.openpgp.encryptMessage(msg.keys, msg.text).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'sign-and-encrypt-message':
|
||||
if (!msg.publicKeys.length) {
|
||||
msg.publicKeys = [msg.publicKeys];
|
||||
}
|
||||
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
|
||||
msg.privateKey = packetlistCloneToKey(msg.privateKey);
|
||||
window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'decrypt-message':
|
||||
msg.privateKey = packetlistCloneToKey(msg.privateKey);
|
||||
msg.message = packetlistCloneToMessage(msg.message.packets);
|
||||
window.openpgp.decryptMessage(msg.privateKey, msg.message).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'decrypt-and-verify-message':
|
||||
msg.privateKey = packetlistCloneToKey(msg.privateKey);
|
||||
if (!msg.publicKeys.length) {
|
||||
msg.publicKeys = [msg.publicKeys];
|
||||
}
|
||||
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
|
||||
msg.message = packetlistCloneToMessage(msg.message.packets);
|
||||
window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'sign-clear-message':
|
||||
msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey);
|
||||
window.openpgp.signClearMessage(msg.privateKeys, msg.text).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'verify-clear-signed-message':
|
||||
if (!msg.publicKeys.length) {
|
||||
msg.publicKeys = [msg.publicKeys];
|
||||
}
|
||||
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
|
||||
var packetlist = window.openpgp.packet.List.fromStructuredClone(msg.message.packets);
|
||||
msg.message = new window.openpgp.cleartext.CleartextMessage(msg.message.text, packetlist);
|
||||
window.openpgp.verifyClearSignedMessage(msg.publicKeys, msg.message).then(function(data) {
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'generate-key-pair':
|
||||
window.openpgp.generateKeyPair(msg.options).then(function(data) {
|
||||
data.key = data.key.toPacketlist();
|
||||
response({event: 'method-return', data: data});
|
||||
}).catch(function(e) {
|
||||
response({event: 'method-return', err: e.message});
|
||||
});
|
||||
break;
|
||||
case 'decrypt-key':
|
||||
try {
|
||||
msg.privateKey = packetlistCloneToKey(msg.privateKey);
|
||||
correct = msg.privateKey.decrypt(msg.password);
|
||||
if (correct) {
|
||||
data = msg.privateKey.toPacketlist();
|
||||
} else {
|
||||
err = 'Wrong password';
|
||||
}
|
||||
} catch (e) {
|
||||
err = e.message;
|
||||
}
|
||||
response({event: 'method-return', data: data, err: err});
|
||||
break;
|
||||
case 'decrypt-key-packet':
|
||||
try {
|
||||
msg.privateKey = packetlistCloneToKey(msg.privateKey);
|
||||
msg.keyIds = msg.keyIds.map(window.openpgp.Keyid.fromClone);
|
||||
correct = msg.privateKey.decryptKeyPacket(msg.keyIds, msg.password);
|
||||
if (correct) {
|
||||
data = msg.privateKey.toPacketlist();
|
||||
} else {
|
||||
err = 'Wrong password';
|
||||
}
|
||||
} catch (e) {
|
||||
err = e.message;
|
||||
}
|
||||
response({event: 'method-return', data: data, err: err});
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown Worker Event.');
|
||||
}
|
||||
};
|
||||
|
||||
function response(event) {
|
||||
if (window.openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) {
|
||||
postMessage({event: 'request-seed'});
|
||||
}
|
||||
postMessage(event);
|
||||
}
|
||||
|
||||
function packetlistCloneToKey(packetlistClone) {
|
||||
var packetlist = window.openpgp.packet.List.fromStructuredClone(packetlistClone);
|
||||
return new window.openpgp.key.Key(packetlist);
|
||||
}
|
||||
|
||||
function packetlistCloneToMessage(packetlistClone) {
|
||||
var packetlist = window.openpgp.packet.List.fromStructuredClone(packetlistClone);
|
||||
return new window.openpgp.message.Message(packetlist);
|
||||
}
|
||||
},{}]},{},[1])
|
||||
;
|
||||
@@ -117,11 +117,10 @@ function success(text) {
|
||||
@param msg The success message text */
|
||||
function status(id, msg) {
|
||||
console.log("state: "+id);
|
||||
if (msg) success(msg); else $("#status").fadeOut('slow');
|
||||
if (msg) success(msg); else $("#status").hide();
|
||||
$("#main").children(":not(#"+id+")").hide();
|
||||
$("#main #"+id).fadeIn("slow", function() {
|
||||
$("#main #"+id+" form input:first-child").focus();
|
||||
});
|
||||
$("#main #"+id).show();
|
||||
$("#main #"+id+" form input:first-child").focus();
|
||||
}
|
||||
|
||||
function emit(signal, data) {
|
||||
@@ -147,10 +146,18 @@ function connectionstatus() {
|
||||
if (socket.connected) connected(); else disconnected();
|
||||
}
|
||||
|
||||
function htmlenc(html) {
|
||||
return $('<div/>').text(html).html();
|
||||
}
|
||||
|
||||
function htmldec(data) {
|
||||
return $('<div/>').html(data).text();
|
||||
}
|
||||
|
||||
/// Alert user
|
||||
/** Alert user, e.g. that a new message has arrived. */
|
||||
function beep(user) {
|
||||
success("message from "+user+" received");
|
||||
if (user) success("message from "+htmlenc(user)+" received");
|
||||
navigator.vibrate =
|
||||
navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate;
|
||||
if (navigator.vibrate) {
|
||||
@@ -313,9 +320,11 @@ function userid() {
|
||||
/// Clear Message Text And Attachments
|
||||
/** Does not remove the receiver's name */
|
||||
function clearmessage() {
|
||||
$("#message").prop(":disabled", true);
|
||||
filecontent = new Array();
|
||||
$('#preview').empty();
|
||||
$("#msg").val("");
|
||||
$("#message").prop(":disabled", false);
|
||||
}
|
||||
|
||||
/// Display Image Attachments
|
||||
@@ -392,8 +401,11 @@ var userMap = null;
|
||||
function users(userlist) {
|
||||
console.log("rcv-> users");
|
||||
userMap = new Array();
|
||||
$("#allusers").empty();
|
||||
userlist.forEach(function(usr) {
|
||||
userMap[usr.name] = usr.pubkey;
|
||||
$("#allusers").append('<option value="'+htmlenc(usr.name)+'">')
|
||||
$("#allusers").hide();
|
||||
console.log(" user: "+usr.name);
|
||||
});
|
||||
localStorage.userMap = JSON.stringify(userMap);
|
||||
@@ -440,6 +452,7 @@ function user(usr) {
|
||||
}
|
||||
if (usr.exists && usr.pubkey && userMap[usr.name] != usr.pubkey) {
|
||||
userMap[usr.name] = usr.pubkey;
|
||||
$("#allusers").append('option value="'+htmlenc(usr.name)+'"')
|
||||
localStorage.userMap = JSON.stringify(userMap);
|
||||
}
|
||||
}
|
||||
@@ -454,13 +467,13 @@ function getPublicKey(user) {
|
||||
}
|
||||
|
||||
/// Received a list of messages from server
|
||||
function messages(ms) {
|
||||
console.log("rcv-> messages");
|
||||
function messages(msgs) {
|
||||
console.log("rcv-> messages("+msgs.length+")");
|
||||
if (!password || !privateKey())
|
||||
return setTimeout(function() {emit("messages");}, 1000); // try again later
|
||||
status("allmessages");
|
||||
notice("load messages, please wait …");
|
||||
ms.forEach(function(msg) {
|
||||
message(msg, false);
|
||||
});
|
||||
success("ready");
|
||||
msgs.forEach(function(msg) {message(msg, false);});
|
||||
status("chat");
|
||||
}
|
||||
|
||||
@@ -509,12 +522,12 @@ function message(m, signaling=true) {
|
||||
the receiver's public key, then send it to the server. */
|
||||
function sendmessage(recv, txt) {
|
||||
notice("1/3 preparing message …");
|
||||
$("#message").fadeOut("slow");
|
||||
$("#message").prop(":disabled", true);
|
||||
getPublicKey(recv) // get receiver's public key
|
||||
.done(function(pk) {
|
||||
var key=openpgp.key.readArmored(pk);
|
||||
if (!pk||key.err) {
|
||||
$("#message").fadeIn("slow");
|
||||
$("#message").prop(":disabled", false);
|
||||
error("receiver's key not found", true);
|
||||
return;
|
||||
}
|
||||
@@ -529,15 +542,14 @@ function sendmessage(recv, txt) {
|
||||
clearmessage();
|
||||
})
|
||||
.catch(function(e) {
|
||||
$("#message").fadeIn("slow");
|
||||
$("#message").prop(":disabled", false);
|
||||
error("encryption of message failed", true);
|
||||
});
|
||||
})
|
||||
.fail(function(e) {
|
||||
$("#message").fadeIn("slow");
|
||||
$("#message").prop(":disabled", false);
|
||||
error("user not found", true);
|
||||
});
|
||||
$("#message").fadeIn("slow");
|
||||
}
|
||||
|
||||
/// Check And Set Password
|
||||
@@ -583,12 +595,15 @@ function removeKey() {
|
||||
/// Main Chat Window
|
||||
/** Gets chat widgets from server and displays them. Starts timer for
|
||||
get() which polls for new messages. */
|
||||
var firsttime = true;
|
||||
function chat() {
|
||||
if (!password) return getpwd();
|
||||
if ($('#msgs').is(':empty'))
|
||||
status("chat");
|
||||
if (firsttime && $('#msgs').is(':empty')) {
|
||||
firsttime = false;
|
||||
notice("getting previous messages, please wait …");
|
||||
emit("messages");
|
||||
else
|
||||
status("chat");
|
||||
}
|
||||
}
|
||||
|
||||
/// Login User
|
||||
@@ -670,6 +685,10 @@ function init() {
|
||||
socket.on("message", message);
|
||||
socket.on("messages", messages);
|
||||
connectionstatus();
|
||||
if (openpgp.initWorker("javascripts/openpgp.worker.js"))
|
||||
console.log("asynchronous openpgp enabled");
|
||||
else
|
||||
console.log("asynchronous openpgp failed");
|
||||
emit('users');
|
||||
start();
|
||||
}
|
||||
|
||||
@@ -188,6 +188,11 @@ td:last-child {
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
:disabled {
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=40); /* MSIE */
|
||||
background-color: lightgray;
|
||||
}
|
||||
label[for=send] img {
|
||||
opacity: 0.4;
|
||||
filter: alpha(opacity=40); /* MSIE */
|
||||
|
||||
@@ -43,7 +43,7 @@ module.exports = function(sql) {
|
||||
|
||||
socket.on("messages", function(msg) {
|
||||
console.log("-> signal: messages");
|
||||
sql.query("select * from message, user where message.user = user.name", [],
|
||||
sql.query("select * from message, user where message.user = user.name order by message.id", [],
|
||||
function(err, res, flds) {
|
||||
emit('messages', res);
|
||||
});
|
||||
|
||||
+10
-1
@@ -46,6 +46,7 @@
|
||||
|
||||
<div id="main">
|
||||
|
||||
<!-- Register New User -->
|
||||
<div id="newuser" style="display: none">
|
||||
<h2>Register User</h2>
|
||||
<p>All you need to start is a username and a password:</p>
|
||||
@@ -99,6 +100,7 @@
|
||||
up your keys.</p>
|
||||
</div>
|
||||
|
||||
<!-- Enter Password -->
|
||||
<div id="getpwd" style="display: none">
|
||||
<form>
|
||||
<input placeholder="please enter password" id="pwd" oninput="setpw(this.value)"
|
||||
@@ -106,10 +108,12 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Chat Screen -->
|
||||
<div id="chat" style="display: none">
|
||||
<div id="message">
|
||||
<form id="chat" autocomplete="off" onsubmit="sendmessage(this.elements['recv'].value, this.elements['msg'].value)">
|
||||
<input placeholder="receiver" autocomplete="off" type="text" id="recv" oninput="checkpartner(this.value)" />
|
||||
<input placeholder="receiver" autocomplete="off" type="text" list="allusers" id="recv" oninput="checkpartner(this.value)" />
|
||||
<datalist id="allusers"></datalist>
|
||||
<input placeholder="message" autocomplete="off" type="text" id="msg"/>
|
||||
<div class="buttongroup">
|
||||
<span class="toolbutton">
|
||||
@@ -149,6 +153,7 @@
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- Password Forgotten -->
|
||||
<div id="forgotpassword" style="display: none">
|
||||
<h2>Password Forgotten</h2>
|
||||
<div class="warning"><strong>Warning!</strong>
|
||||
@@ -174,6 +179,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error: Missing JavaScript -->
|
||||
<noscript>
|
||||
<h2>JavasScript Required!</h2>
|
||||
<p>This is a secure and encryptet chat application, that runs
|
||||
@@ -184,15 +190,18 @@
|
||||
<p><a href="<%= projecturl %>" target="_blank">more information</a></p>
|
||||
</noscript>
|
||||
|
||||
<!-- Error: Missing LocalStorage -->
|
||||
<div id="nolocalstorage" style="display: none">
|
||||
<p>No access to local storage. Please allow access to local
|
||||
storage, i.e. do not block cookies.<p>
|
||||
</div>
|
||||
|
||||
<!-- Notice: Setup Messages -->
|
||||
<div id="allmessages" style="display: none">
|
||||
<p>Setting up all previous messages, please wait …</p>
|
||||
</div>
|
||||
|
||||
<!-- Notice: Startup -->
|
||||
<div id="startup">
|
||||
<p>Starting up …</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user