end to end encryption

stable
Marcus 2020-11-13 10:19:44 -05:00
parent cbf1efb688
commit 4aa755f9ac
3 changed files with 211 additions and 141 deletions

View File

@ -10,64 +10,59 @@ module.exports.Identity = class Identity {
key;
name;
/// ASYNC CONSTRUCTOR
constructor(module, id) {
const kv = new Keyv({
store: new KeyvFile({
filename: `${os.tmpdir()}/valnet/${module}/${id}.json`
})
return new Promise(async (res, rej) => {
const kv = new Keyv({
store: new KeyvFile({
filename: `${os.tmpdir()}/valnet/${module}/${id}.json`
})
});
log = log.scope(`Identity(${module}/${id})`);
this.key = await new Promise(async (res) => {
log.info(`Searching for identity`);
if(! await kv.get('private-key')
|| ! await kv.get('public-key')
|| ! await kv.get('name')) {
log.warn(`no keypair found, generating...`);
const name = hri.random();
const key = new NodeRSA({b: 512});
key.generateKeyPair();
await kv.set('name', name);
await kv.set('private-key', key.exportKey('pkcs8-private-pem'));
await kv.set('public-key', key.exportKey('pkcs8-public-pem'));
log.success(`done!`);
}
const identity = new NodeRSA();
identity.importKey(await kv.get('private-key'), 'pkcs8-private-pem');
identity.importKey(await kv.get('public-key'), 'pkcs8-public-pem');
log.info(`Identity imported.`);
this.name = await kv.get('name');
res(identity);
});
res(this);
});
}
log = log.scope(`Identity(${module}/${id})`);
this.key = new Promise(async (res) => {
log.info(`Searching for identity`);
if(! await kv.get('private-key')
|| ! await kv.get('public-key')
|| ! await kv.get('name')) {
log.warn(`no keypair found, generating...`);
const name = hri.random();
const key = new NodeRSA({b: 512});
key.generateKeyPair();
await kv.set('name', name);
await kv.set('private-key', key.exportKey('pkcs8-private-pem'));
await kv.set('public-key', key.exportKey('pkcs8-public-pem'));
log.success(`done!`);
}
const identity = new NodeRSA();
identity.importKey(await kv.get('private-key'), 'pkcs8-private-pem');
identity.importKey(await kv.get('public-key'), 'pkcs8-public-pem');
log.info(`Identity imported.`);
this.name = await kv.get('name');
res(identity);
});
get publicKey() {
return this.key.exportKey('pkcs8-public-pem');
}
async name() {
await this.key;
return this.name;
}
async encryptPublic(...args) {
await this.key;
async encrypt(...args) {
return this.key.encrypt(...args);
}
async encryptPrivate(...args) {
await this.key;
return this.key.encryptPrivate(...args);
}
async decryptPublic(...args) {
await this.key;
return this.key.decryptPublic(...args);
}
async decryptPrivate(...args) {
await this.key;
async decrypt(...args) {
return this.key.decrypt(...args);
}

View File

@ -1,87 +1,143 @@
const net = require('net');
const EventEmitter = require('events');
const NodeRSA = require('node-rsa');
const log = require('signale');
const log = require('signale').scope('stp');
module.exports.createServer = function(keys, onConnect) {
const server = new Server(keys);
server.on('connect', onConnect);
server.on('connection', onConnect);
return server;
// return 5;
}
module.exports.connect = function(port, ip) {
const client = new Client(port, ip);
}
class Client extends EventEmitter {
port;
ip;
tcpClient
constructor(port, ip) {
super();
this.ip = ip;
this.port = port;
this.tcpClient = net.createConnection(port, ip);
this.tcpClient.on('connect', this.connect.bind(this));
this.tcpClient.on('error', this.error.bind(this));
}
connect(e) {
// log.debug('connect', e);
this.emit('connect');
}
error(e) {
// log.debug('error', e);
this.emit('error');
}
module.exports.connect = function(identity, port, ip) {
const client = net.connect(port, ip);
return new STPSocket(client, identity);
}
class Server extends EventEmitter {
tcpServer;
key;
publicKey;
privateKey;
identity;
Client = class Client extends EventEmitter {
tcpClient;
get remoteAddress() { return this.tcpClient.remoteAddress };
constructor(tcpClient) {
super();
this.tcpClient = tcpClient;
this.tcpClient.on('error', (e) => {
this.emit('error', e)
});
}
write(...args) {
this.tcpClient.write(...args);
}
};
constructor(keys) {
constructor(identity) {
super();
this.identity = identity;
this.tcpServer = net.createServer(this.tcpConnectClient.bind(this));
this.key = new NodeRSA();
this.key.importKey(keys.privateKey, 'pkcs8-private-pem');
this.key.importKey(keys.publicKey, 'pkcs8-public-pem');
this.publicKey = keys.publicKey;
this.privateKey = keys.privateKey;
}
tcpConnectClient(tcpClient) {
const client = new this.Client(tcpClient);
client.write(this.publicKey);
this.emit('connect', client);
// return client;
tcpConnectClient(tcpSocket) {
const socket = new STPSocket(tcpSocket, this.identity);
socket.on('ready', () => {
this.emit('connection', socket);
})
}
listen(...args) {
// log.success('STP Server created on port', args[0]);
this.tcpServer.listen(...args);
}
}
class STPSocket extends EventEmitter {
tcpSocket;
readyState = 0;
buffer = '';
externalIdentity;
get remoteAddress() {
return this.tcpSocket.remoteAddress;
}
get remoteIdentity() {
return this.externalIdentity.exportKey('pkcs8-public-pem');
}
constructor(tcpSocket, identity) {
super();
this.tcpSocket = tcpSocket;
this.identity = identity;
if(tcpSocket.readyState === 'open') this.handshake();
else this.tcpSocket.on('connect', this.handshake.bind(this));
this.tcpSocket.on('data', this.data.bind(this));
}
data(evt) {
// log.debug(evt.toString());
this.buffer += evt.toString();
this.processBuffer();
}
processBuffer() {
const parts = this.buffer.split(/(\x02[^\x02\x03]*\x03)/g);
this.buffer = '';
for(const message of parts) {
if(message.endsWith('\x03')) {
const obj = JSON.parse(message.substr(1, message.length - 2));
this.processMessage(obj);
} else {
this.buffer += message;
}
}
}
processMessage(obj) {
switch(obj.cmd) {
case 'KEY': {
if(this.readyState === 0) {
// log.debug('registering external key');
this.externalIdentity = new NodeRSA();
this.externalIdentity.importKey(obj.data.key, 'pkcs8-public-pem');
this.tcpSocket.write(new AckPacket().toBuffer());
this.readyState = 1;
}
break;
}
case 'ACK': {
if(this.readyState === 1) {
this.readyState = 2;
// log.debug('socket ready!');
this.emit('ready');
}
break;
}
}
}
handshake() {
// log.debug('begin handshake on socket');
const pk = this.identity.publicKey;
const packet = new KeyExchangePacket(pk);
const buffer = packet.toBuffer();
this.tcpSocket.write(buffer);
}
}
class STPPacket {
cmd = 'NOOP';
data = {};
meta = {};
toBuffer() {
return Buffer.from(`\x02${JSON.stringify({
cmd: this.cmd,
data: this.data,
meta: this.meta
})}\x03`);
}
}
class KeyExchangePacket extends STPPacket {
constructor(key, type = 'pkcs8-pem') {
super();
this.cmd = 'KEY';
this.data.key = key;
this.meta.type = type;
}
}
class AckPacket extends STPPacket {
constructor() {
super();
this.cmd = 'ACK';
}
}

View File

@ -1,47 +1,51 @@
(async () => {
const { title } = require('../lib/title');
const net = require('net');
const log = require('signale').scope('relay');
const { config } = require('./../package.json');
const { config } = require('../package.json');
const { Identity } = require('../lib/Identity');
const stp = require('../lib/STP');
title('relay', false);
const identity = new Identity('relay', 'default');
const identity = await new Identity('relay', 'default');
// let connection = null;
// (function tryConnect() {
// connection = net.createConnection(config.ports.ns, 'valnet.xyz', () => {
// log.success('Connected to name server');
// });
// connection.on('error', () => {
// log.error('Could not connect to name server')
// connection = null;
// tryConnect();
// });
// connection.on('data', (data) => {
// log.debug(data.toString());
// })
// })();
(async () => {
await identity.key;
const server = stp.createServer({
publicKey: (await identity.key).exportKey('pkcs8-public-pem'),
privateKey: (await identity.key).exportKey('pkcs8-private-pem')
}, (client) => {
log.info(`incomming connection from ${client.remoteAddress}`);
// ==================================== [STP CLIENT]
let client = null;
(function tryConnect() {
client = stp.connect(identity, config.ports.relay, config.addresses.relay);
client.on('ready', () => {
log.success(`connected to ${config.addresses.relay}`);
})
client.on('error', () => {
log.error(`connection error on ${config.addresses.relay}`)
client = null;
setTimeout(tryConnect, 1000);
});
server.listen(config.ports.relay);
log.success(`STP server listening on ${config.ports.relay}`);
stp.connect(config.ports.relay, config.addresses.relay);
client.on('data', (data) => {
log.debug(data.toString());
})
})();
// stp.connect(config.ports.relay, config.addresses.relay);
// ==================================== [STP SERVER]
const server = stp.createServer(identity, (client) => {
log.info(`incomming connection from ${client.remoteAddress}
${client.remoteIdentity}`);
});
server.listen(config.ports.relay);
log.success(`STP server listening on ${config.ports.relay}`);
// ==================================== [EXPRESS]
const express = require('express');
const app = express();
@ -49,4 +53,19 @@ app.get('/', (req, res) => {
res.end(Date.now().toString());
})
app.listen(9999);
app.listen(9999);
})();