Fully end to end encrypted anonymous chat program. Server only stores public key lookup for users and the encrypted messages. No credentials are transfered to the server, but kept in local browser storage. This allows 100% safe chatting. https://safechat.ch
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
9.0 KiB

9 years ago
var password = null;
var username = null;
function error(data, stay) {
$("#status").fadeOut("slow", function() {
if (data) {
if (typeof data == 'string') {
$("#status").html('<div class="error"><p>error</p><p>'
+data+'</p></div>');
console.log("error: "+data);
} else {
$("#status").html('<div class="error"><p>error</p><pre>'
+JSON.stringify(data)+'</pre></div>');
console.log("error: "+JSON.stringify(data));
}
} else {
$("#status").html('<div class="error"><p>error</p></div>');
console.log("error");
}
$("#status").fadeIn("slow");
if (!stay) setTimeout(start, 5000);
});
}
function notice(text) {
$("#status").fadeOut("slow", function() {
if (text) {
$("#status").html('<div class="notice"><p>notice</p><p>'
+text+'</p></div>');
console.log("notice: "+text);
} else {
$("#status").html('');
console.log("notice");
}
$("#status").fadeIn("slow");
});
}
function success(text) {
$("#status").fadeOut("slow", function() {
if (text) {
$("#status").html('<div class="success"><p>success</p><p>'
+text+'</p></div>');
console.log("success: "+text);
} else {
$("#status").html('');
console.log("success");
}
$("#status").fadeIn("slow");
});
}
function status(text, msg) {
$("#main").fadeOut("slow", function() {
$("#main").html(text);
success(msg);
$("#main").fadeIn("slow");
});
}
function checkuser(user) {
$("#register").submit(function(event) {
return false;
});
$.post("checknewuser.php", {user: user})
.done(function(res) {
username=JSON.parse(res);
if (!username||username.length<1) username=null;
$("#createuser").prop("disabled", !(username && password));
if (username) {
if (password) success("user is ready to be created");
else notice("user name is available, please set password");
} else notice("user name is not available");
}).fail(function(res) {
username=null;
$("#createuser").prop("disabled", !(username && password));
error(res);
});
}
function checkpwd(pwd, pwd2) {
$("#register").submit(function(event) {
return false;
});
if (pwd==pwd2) password=pwd;
else password=null;
if (!password||password.length<1) password=null;
$("#createuser").prop("disabled", !(username && password));
if (password) {
if (username) success("user is ready to be created");
else notice("password matches, please chose a valid user name");
} else notice("passwords don't match");
}
function checkpartner(user) {
$("#chat").submit(function(event) {
return false;
});
$.post("checknewuser.php", {user: user})
.done(function(res) {
if (JSON.parse(res)) {
notice("receiver does not exist");
$("#send").prop("disabled", true);
return;
}
$("#send").prop("disabled", false);
success("receiver exists");
}).fail(function(res) {
notice("cannot connect to server: "+res);
$("#send").prop("disabled", true);
});
}
function createNewUser(user, pwd) {
status("generate keys");
openpgp.generateKeyPair({
numBits: 1024,
userId: user,
passphrase: pwd
}).then(function(keyPair) {
success("keys generated");
localStorage.pubKey = keyPair.publicKeyArmored;
localStorage.privKey = keyPair.privateKeyArmored;
login();
}).catch(function(e) {
error(e);
});
}
function publicKey() {
if (typeof localStorage.pubKey == 'undefined') return null;
return openpgp.key.readArmored(localStorage.pubKey);
}
function privateKey() {
if (typeof localStorage.privKey == 'undefined') return null;
return openpgp.key.readArmored(localStorage.privKey);
}
function userid() {
if (!publicKey() ||
publicKey().keys.length < 1 ||
publicKey().keys[0].getUserIds().length < 1) return null
return publicKey().keys[0].getUserIds()[0];
}
function setreceiver(name) {
$("#recv").val(name);
checkpartner(name);
$("#msg").focus();
}
var startmsg = 0;
function get() {
$.post("get.php", {start: startmsg}).done(function(res) {
var msgs = JSON.parse(res);
if (msgs) {
msgs.forEach(function(e) {
if (startmsg<Number(e["id"])) startmsg = Number(e["id"]);
$.post("pubkey.php", {user: e["user"]}).done(function(pk) {
var res=JSON.parse(pk);
var key=openpgp.key.readArmored(res);
if (!res||key.err) {
error("key of receiver not found", true);
} else {
var message = openpgp.message.readArmored(e["msg"]);
var privkey = privateKey().keys[0];
if (privkey.decrypt(password))
openpgp.decryptAndVerifyMessage(privkey, key.keys, message)
.then(function(msg) {
$("#msgs")
.prepend("<tr><td>"
+e["id"]
+"</td><td>"
+(new Date(1000*Number(e["time"]))).toLocaleString()
+'</td><td><a href="javascript:void(0)" onclick="setreceiver(this.innerHTML)">'
+e["user"]
+"</a></td><td>"
+(msg.signatures[0].valid?"✔":"✘")
+'</td><td class="msg">'
+msg.text
+"</td></tr>");
$(".msg").emoticonize();
});
// .catch(function(e) {
// error("decryption of message failed", true);
// });
}
}).fail(function(e) {
error("get sender's key from server failed", true);
});
});
}
setTimeout(get, 10000);
}).fail(error);
}
function sendmessage(recv, txt) {
$.post("pubkey.php", {user: recv}).done(function(pk) {
var res=JSON.parse(pk);
var key=openpgp.key.readArmored(res);
if (!res||key.err) {
error("key of receiver not found", true);
return;
}
var privkey = privateKey().keys[0];
privkey.decrypt(password);
openpgp.signAndEncryptMessage(key.keys.concat(publicKey().keys), privkey, txt)
.then(function(msg) {
$.post("send.php", {user: userid(), msg: msg})
.done(function(res) {
if (JSON.parse(res)) {
$("#msg").val("");
success("message sent");
} else error("error sending message", true);
})
.fail(error);
})
.catch(function(e) {
error("encryption of message failed", true);
});
}).fail(function(e) {
error("get receiver's key from server failed", true);
});
}
function setpw(pwd) {
if (privateKey().keys[0].decrypt(pwd)) {
password = pwd;
chat();
}
}
function getpwd() {
status('<form>'+
' <label for="pwd">password for '+userid()+':</label>'+
' <input id="pwd" oninput="setpw(this.value)" type="password" autofocus/>'+
'</form>');
}
function chat() {
if (!password) return getpwd();
$.ajax({url: "chat.html", success: function(res) {
status(res);
setTimeout(get, 2000);
}}).fail(error);
}
function login() {
status("login ...");
$.post("login.php", {user: userid(),
pubkey: localStorage.pubKey},
function(res) {
if (JSON.parse(res)) {
status("logged in ...", "successfully logged in");
chat();
} else {
error("login failed");
}
}).fail(function(e) {
error(e);
});
}
function newuser() {
status("new user ...");
$.ajax({url: "newuser.html", success: function(res) {
status(res);
}}).fail(error);
}
function start() {
try {
status("Starting up ...");
if (!userid()) {
newuser();
} else {
login();
}
} catch (m) {
error(m);
}
}
$(start);