end to end encryption
parent
cbf1efb688
commit
4aa755f9ac
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
180
lib/STP.js
180
lib/STP.js
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})();
|
||||
Reference in New Issue