|
|
@ -20,8 +20,8 @@ |
|
|
|
start -> newuser [label="if no keys exist"]; |
|
|
|
start -> newuser [label="if no keys exist"]; |
|
|
|
start -> login [label="if keys exist"]; |
|
|
|
start -> login [label="if keys exist"]; |
|
|
|
newuser -> createkeypair [label="on submit"]; |
|
|
|
newuser -> createkeypair [label="on submit"]; |
|
|
|
createkeypair -> "openpgp.generateKeyPair"; |
|
|
|
createkeypair -> "openpgp.generateKey"; |
|
|
|
"openpgp.generateKeyPair" -> login [label="keys generated in local store"]; |
|
|
|
"openpgp.generateKey" -> login [label="keys generated in local store"]; |
|
|
|
login -> chat [label="user is valid on server"]; |
|
|
|
login -> chat [label="user is valid on server"]; |
|
|
|
chat -> getpwd [label="password not yet entered"]; |
|
|
|
chat -> getpwd [label="password not yet entered"]; |
|
|
|
getpwd -> setpw [label="on input"]; |
|
|
|
getpwd -> setpw [label="on input"]; |
|
|
@ -33,20 +33,88 @@ |
|
|
|
sendmessage -> chat [label="remain in chat"]; |
|
|
|
sendmessage -> chat [label="remain in chat"]; |
|
|
|
} |
|
|
|
} |
|
|
|
@enddot |
|
|
|
@enddot |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
// 1 2 3 4 5 6 7 8
|
|
|
|
// 1 2 3 4 5 6 7 8
|
|
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function SafeChatClient(success, notice, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Cache Client's Key from local Strorage
|
|
|
|
|
|
|
|
var k = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Get User Key
|
|
|
|
|
|
|
|
/** @internal key ist cached in k |
|
|
|
|
|
|
|
@return key */ |
|
|
|
|
|
|
|
function key() { |
|
|
|
|
|
|
|
if (k) return k |
|
|
|
|
|
|
|
if (typeof localStorage.key == 'undefined') return null |
|
|
|
|
|
|
|
return k = openpgp.key.readArmored(localStorage.key) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Get Own User Name
|
|
|
|
|
|
|
|
/** Get user name as user id of first public key */ |
|
|
|
|
|
|
|
function uid() { |
|
|
|
|
|
|
|
if (k || key()) return k.pub.keys[0].getUserIds()[0] |
|
|
|
|
|
|
|
return null |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Create New User
|
|
|
|
|
|
|
|
function createuser(user, email, pwd) { |
|
|
|
|
|
|
|
notice("generating keys") |
|
|
|
|
|
|
|
openpgp.generateKey({ |
|
|
|
|
|
|
|
numBits: 4096, |
|
|
|
|
|
|
|
userIds: [{name: user, email: email}], |
|
|
|
|
|
|
|
passphrase: pwd |
|
|
|
|
|
|
|
}).then(function(keyPair) { |
|
|
|
|
|
|
|
success("keys generated") |
|
|
|
|
|
|
|
localStorage.key = keyPair.privateKeyArmored |
|
|
|
|
|
|
|
k = keyPair.key |
|
|
|
|
|
|
|
}).catch(function(e) { |
|
|
|
|
|
|
|
console.log(e) |
|
|
|
|
|
|
|
error("generating key pairs failed") |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function password(pwd) { |
|
|
|
|
|
|
|
return (k || keys()) && k.keys[0].decrypt(pwd) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Encrypt Message
|
|
|
|
|
|
|
|
function encrypt(targetkeys, message, done, failed) { |
|
|
|
|
|
|
|
if (!k) return false |
|
|
|
|
|
|
|
openpgp.encrypt({ |
|
|
|
|
|
|
|
publicKeys: targets.keys.concat(k.keys), |
|
|
|
|
|
|
|
privateKeys: k, |
|
|
|
|
|
|
|
data: message, |
|
|
|
|
|
|
|
armor: false}) |
|
|
|
|
|
|
|
.then(done) |
|
|
|
|
|
|
|
.else(failed) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Decrypt Message
|
|
|
|
|
|
|
|
function decrypt(message) { |
|
|
|
|
|
|
|
if (!k) return false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var password = null; ///< password, only stored temporary, until reload
|
|
|
|
var password = null; ///< password, only stored temporary, until reload
|
|
|
|
var username = null; ///< username, only used during registration
|
|
|
|
var username = null; ///< username, only used during registration
|
|
|
|
var filecontent = new Array(); ///< temporary storage for attachments
|
|
|
|
var filecontent = new Array(); ///< temporary storage for attachments
|
|
|
|
var socket = io.connect(); |
|
|
|
var socket = io.connect(); |
|
|
|
|
|
|
|
var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch'; |
|
|
|
|
|
|
|
|
|
|
|
/// Padding for numbers in dates
|
|
|
|
/// Padding for numbers in dates
|
|
|
|
function pad(n) { |
|
|
|
function pad(n) { |
|
|
|
return n<10 ? '0'+n : n |
|
|
|
return n<10 ? '0'+n : n |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function uid(name) { |
|
|
|
|
|
|
|
return name+' <'+name+'@'+hostname+'>'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Convert number of bytes to readable text
|
|
|
|
/// Convert number of bytes to readable text
|
|
|
|
function size(num) { |
|
|
|
function size(num) { |
|
|
|
if (num>0.6*1024) { |
|
|
|
if (num>0.6*1024) { |
|
|
@ -206,7 +274,7 @@ function backup() { |
|
|
|
var now = new Date(); |
|
|
|
var now = new Date(); |
|
|
|
download.download = |
|
|
|
download.download = |
|
|
|
pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+ |
|
|
|
pad(now.getFullYear())+pad(now.getMonth()+1)+pad(now.getDate())+ |
|
|
|
"-"+userid()+"@"+window.location.hostname+".bak"; |
|
|
|
"-"+userid()+"@"+hostname+".bak"; |
|
|
|
var clickEvent = new MouseEvent("click", { |
|
|
|
var clickEvent = new MouseEvent("click", { |
|
|
|
"view": window, |
|
|
|
"view": window, |
|
|
|
"bubbles": true, |
|
|
|
"bubbles": true, |
|
|
@ -284,16 +352,16 @@ function checkpartner(user) { |
|
|
|
$("#chat").submit(function(event) { |
|
|
|
$("#chat").submit(function(event) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
}); |
|
|
|
}); |
|
|
|
emit("user", user); |
|
|
|
emit("user", uid(user)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Create Local Public-/Private-Key Pair
|
|
|
|
/// Create Local Public-/Private-Key Pair
|
|
|
|
/** Called if user has not yet his keys, just generates a new key pair. */ |
|
|
|
/** Called if user has not yet his keys, just generates a new key pair. */ |
|
|
|
function createkeypair(user, pwd) { |
|
|
|
function createkeypair(user, pwd) { |
|
|
|
notice("generating keys"); |
|
|
|
notice("generating keys"); |
|
|
|
openpgp.generateKeyPair({ |
|
|
|
openpgp.generateKey({ |
|
|
|
numBits: 4096, |
|
|
|
numBits: 4096, |
|
|
|
userId: user, |
|
|
|
userIds: [{name: user, email: user+'@'+hostname}], |
|
|
|
passphrase: pwd |
|
|
|
passphrase: pwd |
|
|
|
}).then(function(keyPair) { |
|
|
|
}).then(function(keyPair) { |
|
|
|
success("keys generated"); |
|
|
|
success("keys generated"); |
|
|
@ -301,7 +369,8 @@ function createkeypair(user, pwd) { |
|
|
|
localStorage.privkey = keyPair.privateKeyArmored; |
|
|
|
localStorage.privkey = keyPair.privateKeyArmored; |
|
|
|
login(); |
|
|
|
login(); |
|
|
|
}).catch(function(e) { |
|
|
|
}).catch(function(e) { |
|
|
|
error("generating key pairs failed"); |
|
|
|
console.log(e) |
|
|
|
|
|
|
|
error("generating key pairs failed") |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -357,7 +426,7 @@ function guessfilename(mimetype, user, date) { |
|
|
|
if (!date) date = new Date(); |
|
|
|
if (!date) date = new Date(); |
|
|
|
var ext = mimetype.replace(/.*\/(x-)?/i, ""); |
|
|
|
var ext = mimetype.replace(/.*\/(x-)?/i, ""); |
|
|
|
return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate()) |
|
|
|
return pad(date.getFullYear())+pad(date.getMonth()+1)+pad(date.getDate()) |
|
|
|
+"-"+ext+"-"+user+"@"+window.location.hostname+'.'+ext; |
|
|
|
+"-"+ext+"-"+user+"@"+hostname+'.'+ext; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Display Image Attachments
|
|
|
|
/// Display Image Attachments
|
|
|
@ -545,7 +614,7 @@ function loggedin() { |
|
|
|
function user(usr) { |
|
|
|
function user(usr) { |
|
|
|
if (usr.exists) console.log("rcv-> user("+usr.name+")"); |
|
|
|
if (usr.exists) console.log("rcv-> user("+usr.name+")"); |
|
|
|
else console.log("rcv-> user("+usr.name+"): name is available"); |
|
|
|
else console.log("rcv-> user("+usr.name+"): name is available"); |
|
|
|
if ($("#newuser").is(":visible") && usr.name==$('#user').val()) { |
|
|
|
if ($("#newuser").is(":visible") && usr.name==uid($('#user').val())) { |
|
|
|
// same username as in the create user form
|
|
|
|
// same username as in the create user form
|
|
|
|
$("#createuser").prop("disabled", usr.exists); // todo: check password
|
|
|
|
$("#createuser").prop("disabled", usr.exists); // todo: check password
|
|
|
|
if (!usr.exists) { |
|
|
|
if (!usr.exists) { |
|
|
@ -556,7 +625,7 @@ function user(usr) { |
|
|
|
error("user name "+usr.name+" is in use", true); |
|
|
|
error("user name "+usr.name+" is in use", true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if ($("#chat").is(":visible") && usr.name==$("#recv").val()) { // same username as in receiver
|
|
|
|
if ($("#chat").is(":visible") && usr.name==uid($("#recv").val())) { // same username as in receiver
|
|
|
|
$('#send').prop("disabled", !usr.exists); |
|
|
|
$('#send').prop("disabled", !usr.exists); |
|
|
|
$("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4"); |
|
|
|
$("label[for=send] img").css("opacity", usr.exists?"1.0":"0.4"); |
|
|
|
$("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)"); |
|
|
|
$("label[for=send] img").css("filter", usr.exists?"alpha(opacity=100)":"alpha(opacity=40)"); |
|
|
@ -577,6 +646,11 @@ function user(usr) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function queryuser(usr) { |
|
|
|
|
|
|
|
console.log("query user: "+uid(usr)); |
|
|
|
|
|
|
|
socket.emit("user", uid(usr)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Get a user's public key.
|
|
|
|
/// Get a user's public key.
|
|
|
|
/** The first time, gets it from the server, later from the cache. */ |
|
|
|
/** The first time, gets it from the server, later from the cache. */ |
|
|
|
function getPublicKey(user) { |
|
|
|
function getPublicKey(user) { |
|
|
@ -606,8 +680,11 @@ function message(m, internal) { |
|
|
|
var message = openpgp.message.readArmored(m.msg); |
|
|
|
var message = openpgp.message.readArmored(m.msg); |
|
|
|
var privkey = privateKey().keys[0]; |
|
|
|
var privkey = privateKey().keys[0]; |
|
|
|
if (privkey.decrypt(password)) // prepare own key
|
|
|
|
if (privkey.decrypt(password)) // prepare own key
|
|
|
|
openpgp.decryptAndVerifyMessage(privkey, key.keys, message) |
|
|
|
openpgp.decrypt({ |
|
|
|
.then(function(msg) { // decryption succeded
|
|
|
|
privateKeys: privkey, |
|
|
|
|
|
|
|
publicKeys: key.keys, |
|
|
|
|
|
|
|
message: message |
|
|
|
|
|
|
|
}).then(function(msg) { // decryption succeded
|
|
|
|
// prepend message to list of messages
|
|
|
|
// prepend message to list of messages
|
|
|
|
var message = JSON.parse(msg.text); |
|
|
|
var message = JSON.parse(msg.text); |
|
|
|
$("#msgs") // todo: check msg.signatures[0].valid
|
|
|
|
$("#msgs") // todo: check msg.signatures[0].valid
|
|
|
@ -632,8 +709,7 @@ function message(m, internal) { |
|
|
|
// calculate and show emoticons
|
|
|
|
// calculate and show emoticons
|
|
|
|
$('#id'+m.id).emoticonize(); |
|
|
|
$('#id'+m.id).emoticonize(); |
|
|
|
if (!internal) beep(m.user); |
|
|
|
if (!internal) beep(m.user); |
|
|
|
}) |
|
|
|
}).catch(function(e) { |
|
|
|
.catch(function(e) { |
|
|
|
|
|
|
|
// not for me
|
|
|
|
// not for me
|
|
|
|
success(); |
|
|
|
success(); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -657,9 +733,10 @@ function sendmessage(recv, txt) { |
|
|
|
privkey.decrypt(password); // get own private key ready
|
|
|
|
privkey.decrypt(password); // get own private key ready
|
|
|
|
var message = JSON.stringify({receiver: recv, text: txt, files: filecontent}); |
|
|
|
var message = JSON.stringify({receiver: recv, text: txt, files: filecontent}); |
|
|
|
notice("2/3 encrypting message …"); |
|
|
|
notice("2/3 encrypting message …"); |
|
|
|
openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys), |
|
|
|
openpgp.encrypt({publicKeys: key.keys.concat(publicKey().keys), |
|
|
|
privkey, |
|
|
|
privateKeys: privkey, |
|
|
|
message) |
|
|
|
data: message, |
|
|
|
|
|
|
|
armor: false}) |
|
|
|
.then(function(msg) { // message is encrypted
|
|
|
|
.then(function(msg) { // message is encrypted
|
|
|
|
notice("3/3 sending message …"); |
|
|
|
notice("3/3 sending message …"); |
|
|
|
emit("message", {user: userid(), content: msg}); |
|
|
|
emit("message", {user: userid(), content: msg}); |
|
|
@ -743,7 +820,7 @@ function chat() { |
|
|
|
different, then this is a complete failure, something went |
|
|
|
different, then this is a complete failure, something went |
|
|
|
terribly wrong. */ |
|
|
|
terribly wrong. */ |
|
|
|
function login() { |
|
|
|
function login() { |
|
|
|
$("#username").html(userid()+"@"+window.location.hostname); |
|
|
|
$("#username").html(userid()+"@"+hostname); |
|
|
|
emit("login", {name: userid(), |
|
|
|
emit("login", {name: userid(), |
|
|
|
pubkey: localStorage.pubkey}); |
|
|
|
pubkey: localStorage.pubkey}); |
|
|
|
success("login sent to server"); |
|
|
|
success("login sent to server"); |
|
|
@ -810,7 +887,7 @@ function init() { |
|
|
|
socket.on("message", message); |
|
|
|
socket.on("message", message); |
|
|
|
socket.on("messages", messages); |
|
|
|
socket.on("messages", messages); |
|
|
|
connectionstatus(); |
|
|
|
connectionstatus(); |
|
|
|
if (openpgp.initWorker("javascripts/openpgp.worker.js")) |
|
|
|
if (openpgp.initWorker("openpgp.worker.min.js")) |
|
|
|
console.log("asynchronous openpgp enabled"); |
|
|
|
console.log("asynchronous openpgp enabled"); |
|
|
|
else |
|
|
|
else |
|
|
|
console.log("asynchronous openpgp failed"); |
|
|
|
console.log("asynchronous openpgp failed"); |
|
|
|