Category: Cryptography

Group Chat chiffré en AES256 avec NodeJS

Cher visiteur,

Il y a quelques temps de cela, je cherchais une messagerie chiffrée utilisable en groupe, après avoir cherché un peu, testé des messageries comme Wire (qui est pour le moment pour ma part une des plus secure que j’ai pu trouver même si ça manque cruellement de fonctionnalités), jabber avec otr ou même omemo et bien d’autres.

Je me suis donc posé la question “et si je m’amusais à refaire un chat de groupe de façon chiffré” ?

Du coup je me suis permis de réaliser cette idée, bien sûr il s’agit d’un PoC et beaucoup pensent qu’implémenter soit même une crypto est risqué (ils ont très certainement raison).

J’ai donc décidé de me faire ma propre implémentation en utilisant comme vecteur d’initialisation (IV), des flux rss publics, de ce fait, l’AES change à chaque fois qu’une nouvelle actualité est publiée sur le flux rss spécifié, pour sécuriser un peu la chose, j’ai rajouté un salt à communiquer entre clients avant la conversation, une meilleure solution serait d’utiliser des certificats mais échanger tous les certificats entre chaque client est plutôt fastidieux.

Server:

var io = require('socket.io').listen(1337);

var allClients = [];

io.sockets.on('connection', function (socket) {
	socket.on('send', function(data) {
		if(data.type == 'connected') {
			socket.nickname = data.content;
			allClients.push(socket);
			socket.broadcast.emit('send', { type : 'connected', content: socket.nickname + " is now connected" });
			socket.emit('send', { type : 'connected', content: socket.nickname + " is now connected" });
		} else if (data.type == 'list') {
			var nicknames = [];
			allClients.forEach(function(socket) {
				nicknames.push(socket.nickname)
			});
			socket.emit('send', { type : 'list', content: nicknames });
		} else if (data.type == 'message') {
			socket.broadcast.emit('send', { type : 'message', content: data.content });
		}
	});

	socket.on('disconnect', function() {
      	var i = allClients.indexOf(socket);
      	allClients.splice(i, 1);
      	socket.broadcast.emit('send', { type : 'disconnect', content: socket.nickname + " is now disconnected" });
   });
});

Client:

var server = 'http://localhost:1337';

var readline = require('readline');
var io = require('socket.io-client');
var socket;
var colors = require('colors');
var parser = require('rss-parser');
var sha256 = require('sha256');
var crypto = require('crypto'), algorithm = 'aes-256-ctr';

var rl = readline.createInterface(process.stdin, process.stdout);

var rss_link = 'https://www.lesechos.fr/rss/rss_une_titres.xml';
var aes_salt = 'YourSaltIfYouWant';

var nickname;
rl.question("Please enter a nickname: ", function(name) {
	nickname = name;
	socket = io.connect(server, {reconnect: true});
    socket.emit('send', { type: 'connected', content: name });

    socket.on('connect', function () { 
		socket.on('send', function(data) {
			if(data.type == 'connected') {
				console.log(colors.green(data.content));
				rl.prompt(true);
			} else if (data.type == 'list') {
				data.content.forEach(function(nickname) {
					console.log(colors.magenta(nickname));
				});
				rl.prompt(true);
			} else  if (data.type == 'message') {
				parser.parseURL(rss_link, function(err, parsed) {
					var iv = sha256(parsed.feed.entries[0].title + parsed.feed.entries[1].title);
					console.log(decrypt(iv + aes_salt, data.content));
					rl.prompt(true);
				});
			} else if (data.type == 'disconnect') {
				console.log(colors.red(data.content));
			}
		});

		rl.on('line', function (line) {
			if(line.indexOf('!help') > -1) {
				console.log(colors.yellow('!list -> Check who is online.'));
				rl.prompt(true);
			} else if(line.indexOf('!list') > -1) {
				socket.emit('send', { type: 'list' });
			} else {
				parser.parseURL(rss_link, function(err, parsed) {
					var iv = sha256(parsed.feed.entries[0].title + parsed.feed.entries[1].title);
					var message = encrypt(iv + aes_salt, nickname + " : " + line);
				  	socket.emit('send', { type: 'message', content: message });
					rl.prompt(true);
				});
			}
		});
	});
});

function encrypt(key, message) {
	var cipher = crypto.createCipher(algorithm,key)
  	var crypted = cipher.update(message,'utf8','hex')
  	crypted += cipher.final('hex');
	return crypted;
}

function decrypt(key, message) {
	var decipher = crypto.createDecipher(algorithm,key)
  	var decrypt = decipher.update(message,'hex','utf8')
  	decrypt += decipher.final('utf8');
	return decrypt;
}