in the middle of rewriting it

master
Marc Wäckerlin 8 years ago
parent d0e32dfcae
commit ded3085d90
  1. 1
      nodejs/database/index.js
  2. 9
      nodejs/etc/systemd/system/safechat.service
  3. 117
      nodejs/public/javascripts/safechat.js
  4. 30
      nodejs/routes/index.js
  5. 21
      nodejs/safechat.js
  6. 1
      nodejs/sockets/index.js
  7. 21
      nodejs/views/index.ejs

@ -3,7 +3,6 @@ module.exports = function(config) {
var fs = require('fs');
config.multipleStatements = true;
var pool = mysql.createPool(config);
console.log(__dirname+'/schema.sql')
pool.query(fs.readFileSync(__dirname+'/schema.sql').toString());
if (config.max_allowed_packet)
pool.query("set global max_allowed_packet=?", [config.max_allowed_packet]);

@ -0,0 +1,9 @@
[Unit]
Description=Secure and Encrypted Chat Server
[Service]
ExecStart=/usr/bin/nodejs /usr/share/safechat/nodejs/safechat > /var/log/safechat.log
Restart=on-abort
[Install]
WantedBy=multi-user.target

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

@ -1,22 +1,14 @@
/*
* GET home page.
*/
var package = require(__dirname+"/../package.json");
exports.index = function(req, res) {
res.render(path, {
projecturl: package.documentation,
packagename: package.name,
packageversion: package.version
module.exports = function(app, package) {
[
'',
'webrtc'
].forEach(function(p) {
app.get('/'+p, function(req, res) {
res.render(p?p:'index', {
package: package
})
})
})
}
exports.webrtc = function(req, res) {
res.render('webrtc', {
projecturl: package.documentation,
packagename: package.name,
packageversion: package.version
});
}

@ -5,8 +5,8 @@
var package = require(__dirname+'/package.json');
var config = require(package.path.config);
var express = require('express');
var routes = require(__dirname+'/routes');
var app = module.exports = express.createServer();
var routes = require(__dirname+'/routes')(app, package);
var io = require('socket.io').listen(app);
var sql = require(__dirname+'/database')(config.mysql);
var sockets = require(__dirname+'/sockets')(sql);
@ -34,11 +34,17 @@ app.configure(function() {
app.use(app.router);
app.use(express.static(__dirname + '/public'));
[
'jquery/dist',
'openpgp/dist'
'jquery/dist/jquery.min.js',
'jquery/dist/jquery.js',
'openpgp/dist/openpgp.min.js',
'openpgp/dist/openpgp.js',
'openpgp/dist/openpgp.worker.min.js',
'openpgp/dist/openpgp.worker.js'
].forEach(function(file) {
app.use('/'+file.replace(/\/.*/g, ''),
express.static(__dirname + '/node_modules/'+file));
app.get('/'+file.replace(/.*\//g, ''),
function(req, res) {
res.sendfile('/node_modules/'+file, {root: __dirname})
})
})
});
@ -54,11 +60,6 @@ app.configure('production', function(){
io.sockets.on('connection', sockets.connection);
// Routes
app.get('/', routes.index);
app.get('/webrtc', routes.webrtc);
app.listen(config.port, function(){
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});

@ -56,6 +56,7 @@ module.exports = function(sql) {
});
socket.on("login", function(user) {
console.log('-> signal: login('+user.name+')');
if (!user.name || !user.pubkey) return emit("fail", "wrong login format");
console.log("-> signal: login("+user.name+")");
if (user.name=="safechat") return emit("fail", "user name safechat is reserved");

@ -4,9 +4,8 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width initial-scale=1" />
<link href="stylesheets/safechat.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="jquery/jquery.min.js"></script>
<script type="text/javascript" src="openpgp/openpgp.min.js"></script>
<script type="text/javascript" src="openpgp/openpgp.worker.min.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="openpgp.min.js"></script>
<script type="text/javascript" src="socket.io/socket.io.js"></script>
<script type="text/javascript" src="javascripts/mediarecorder.js"></script>
<script type="text/javascript" src="javascripts/safechat.js"></script>
@ -18,7 +17,7 @@
<body>
<div id="header" class="header">
<h1>Safe Chat <%= packageversion %></h1>
<h1>Safe Chat <%= package.version %></h1>
<div id="togglemenu">
<span id="username">[unknown]</span>
<span id="connectionstatus">
@ -35,7 +34,7 @@
<li id="groups" onclick="groups()">Edit Groups</li>
<li id="removeKey" style="display: none" onclick="removeKey()">Password Forgotten</li>
<li id="android-download" href="safechat.apk"><a href="safechat.apk">Download Android-App</a></li>
<li href="<%= projecturl %>" target="_blank"><a href="<%= projecturl %>" target="_blank">About Safe Chat</a></li>
<li href="<%= package.documentation %>" target="_blank"><a href="<%= package.documentation %>" target="_blank">About Safe Chat</a></li>
</ul>
<script type="text/javascript">
$(function() { // on load: without cordova, remove andoid-download
@ -56,16 +55,14 @@
<input placeholder="username" autocomplete="off" type="text" id="user"/>
<input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/>
<input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/>
<input id="createuser" type="submit" disabled/>
<button id="createuser" disabled>register</button>
</form>
<script>
$("#user").on("input", function() {
console.log("query user: "+$('#user').val());
socket.emit("user", $('#user').val());
queryuser($('#user').val());
});
$("#register").submit(function(event) {
createkeypair(event.target.elements['user'].value,
event.target.elements['pwd'].value);
$("#createuser").on("click", function(event) {
createkeypair($('#user').val(), $('#pwd').val());
return false;
});
</script>
@ -193,7 +190,7 @@
server. Your password and your secret key are fully under
your control. That's why you must enable javascript and
local storage for this application.</p>
<p><a href="<%= projecturl %>" target="_blank">more information</a></p>
<p><a href="<%= package.documentation %>" target="_blank">more information</a></p>
</noscript>
<!-- Error: Missing LocalStorage -->

Loading…
Cancel
Save