hey, we have basic starttls! Though it's not wired up yet.
This commit is contained in:
parent
90f1a69e2e
commit
a2d2b58b38
9 changed files with 426 additions and 0 deletions
143
package-lock.json
generated
143
package-lock.json
generated
|
@ -12,6 +12,8 @@
|
||||||
"applause-cli": "^1.7.0",
|
"applause-cli": "^1.7.0",
|
||||||
"log": "^6.2.0",
|
"log": "^6.2.0",
|
||||||
"make-cert": "^1.2.1",
|
"make-cert": "^1.2.1",
|
||||||
|
"nexline": "^1.2.2",
|
||||||
|
"systeminformation": "^5.9.4",
|
||||||
"tweetnacl": "^1.0.3"
|
"tweetnacl": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -89,6 +91,43 @@
|
||||||
"type": "^2.5.0"
|
"type": "^2.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"universalify": "^0.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6 <7 || >=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/log": {
|
"node_modules/log": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/log/-/log-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/log/-/log-6.2.0.tgz",
|
||||||
|
@ -113,6 +152,18 @@
|
||||||
"make-cert": "make-cert.js"
|
"make-cert": "make-cert.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nexline": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nexline/-/nexline-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-YLX5uoqNP7XVsXk889i8ZQcuMkukA4My4JD9wqTRLT+4dFo6QEEn+hU26J5H89m+mzW9BfhDgriGdbMEP06eeQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"fs-extra": "^8.1.0",
|
||||||
|
"iconv-lite": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/next-tick": {
|
"node_modules/next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
|
@ -126,6 +177,11 @@
|
||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
"node_modules/sprintf-kit": {
|
"node_modules/sprintf-kit": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz",
|
||||||
|
@ -134,6 +190,30 @@
|
||||||
"es5-ext": "^0.10.53"
|
"es5-ext": "^0.10.53"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/systeminformation": {
|
||||||
|
"version": "5.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.9.4.tgz",
|
||||||
|
"integrity": "sha512-FOsiTn0CyJZoj9kIhla11ndsMzbbwwuriul81wpqIBt9IpbxHZ6P/oZCphIFgJrwqjTnme0Qp1HDzIkUD9Xr/g==",
|
||||||
|
"os": [
|
||||||
|
"darwin",
|
||||||
|
"linux",
|
||||||
|
"win32",
|
||||||
|
"freebsd",
|
||||||
|
"openbsd",
|
||||||
|
"netbsd",
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"systeminformation": "lib/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "Buy me a coffee",
|
||||||
|
"url": "https://www.buymeacoffee.com/systeminfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
|
@ -143,6 +223,14 @@
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
|
||||||
"integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw=="
|
"integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw=="
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -222,6 +310,37 @@
|
||||||
"type": "^2.5.0"
|
"type": "^2.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fs-extra": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"universalify": "^0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"graceful-fs": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"log": {
|
"log": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/log/-/log-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/log/-/log-6.2.0.tgz",
|
||||||
|
@ -243,6 +362,15 @@
|
||||||
"node-forge": "^0.10.0"
|
"node-forge": "^0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nexline": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nexline/-/nexline-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-YLX5uoqNP7XVsXk889i8ZQcuMkukA4My4JD9wqTRLT+4dFo6QEEn+hU26J5H89m+mzW9BfhDgriGdbMEP06eeQ==",
|
||||||
|
"requires": {
|
||||||
|
"fs-extra": "^8.1.0",
|
||||||
|
"iconv-lite": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
|
@ -253,6 +381,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
||||||
},
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
"sprintf-kit": {
|
"sprintf-kit": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz",
|
||||||
|
@ -261,6 +394,11 @@
|
||||||
"es5-ext": "^0.10.53"
|
"es5-ext": "^0.10.53"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systeminformation": {
|
||||||
|
"version": "5.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.9.4.tgz",
|
||||||
|
"integrity": "sha512-FOsiTn0CyJZoj9kIhla11ndsMzbbwwuriul81wpqIBt9IpbxHZ6P/oZCphIFgJrwqjTnme0Qp1HDzIkUD9Xr/g=="
|
||||||
|
},
|
||||||
"tweetnacl": {
|
"tweetnacl": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
|
@ -270,6 +408,11 @@
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
|
||||||
"integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw=="
|
"integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw=="
|
||||||
|
},
|
||||||
|
"universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
"applause-cli": "^1.7.0",
|
"applause-cli": "^1.7.0",
|
||||||
"log": "^6.2.0",
|
"log": "^6.2.0",
|
||||||
"make-cert": "^1.2.1",
|
"make-cert": "^1.2.1",
|
||||||
|
"nexline": "^1.2.2",
|
||||||
|
"systeminformation": "^5.9.4",
|
||||||
"tweetnacl": "^1.0.3"
|
"tweetnacl": "^1.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/lib/agent/Agent.mjs
Normal file
30
src/lib/agent/Agent.mjs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
import make_cert from 'make-cert';
|
||||||
|
import systeminfo from 'systeminformation';
|
||||||
|
|
||||||
|
import hash from '../crypto/hash.mjs';
|
||||||
|
|
||||||
|
class Agent {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(secret_join) {
|
||||||
|
this.secret_join = secret_join;
|
||||||
|
|
||||||
|
/** Our peer id - calculated automatically from the system's uuid */
|
||||||
|
this.peer_id = hash("sha256", "base64", await systeminfo.system().serial)
|
||||||
|
.replace(/[+/=]/g, "");
|
||||||
|
this.peer_name = os.hostname();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Properties: key, cert
|
||||||
|
this.cert = make_cert(`${our_id}.systemquery-peer.localhost`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Agent;
|
9
src/lib/agent/PeerServer.mjs
Normal file
9
src/lib/agent/PeerServer.mjs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
class PeerServer {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PeerServer;
|
9
src/lib/crypto/hash.mjs
Normal file
9
src/lib/crypto/hash.mjs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export default function hash(algorithm, encoding, data) {
|
||||||
|
let hasher = crypto.createHash(algorithm);
|
||||||
|
hasher.update(data);
|
||||||
|
return hasher.digest(encoding);
|
||||||
|
}
|
56
src/lib/crypto/secretbox.mjs
Normal file
56
src/lib/crypto/secretbox.mjs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import { randomBytes, secretbox } from 'tweetnacl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new key ready for encryption.
|
||||||
|
* @return {string} A new base64-encoded key.
|
||||||
|
*/
|
||||||
|
function make_key() {
|
||||||
|
return randomBytes(secretbox.keyLength).toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given data with the given key.
|
||||||
|
* @param {string} key The base64-encoded key to use to encrypt the data.
|
||||||
|
* @param {string} data The data to encrypt.
|
||||||
|
* @return {string} The encrypted data, base64 encoded.
|
||||||
|
*/
|
||||||
|
function encrypt(key, data) {
|
||||||
|
const key_bytes = Buffer.from(key, "base64");
|
||||||
|
const nonce = randomBytes(secretbox.nonceLength);
|
||||||
|
const data_bytes = Buffer.from(data, "utf-8");
|
||||||
|
|
||||||
|
const cipher_bytes = secretbox(data_bytes, nonce, key_bytes);
|
||||||
|
|
||||||
|
const concat_bytes = Buffer.concat([nonce, cipher_bytes]);
|
||||||
|
|
||||||
|
key_bytes.fill(0);
|
||||||
|
nonce.fill(0);
|
||||||
|
|
||||||
|
return concat_bytes.toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given data with the given key.
|
||||||
|
* @param {string} key The base64-encoded keyto use to decrypt the data.
|
||||||
|
* @param {string} cipher_text The base64-encoded ciphertext to decrypt.
|
||||||
|
* @return {string} The decoded data, utf-8 encoded.
|
||||||
|
*/
|
||||||
|
function decrypt(key, cipher_text) {
|
||||||
|
const concat_bytes = Buffer.from(cipher_text, "base64");
|
||||||
|
const key_bytes = Buffer.from(key, "basse64");
|
||||||
|
|
||||||
|
const nonce = concat_bytes.slice(0, secretbox.nonceLength);
|
||||||
|
const cipher_bytes = concat_bytes.slice(secretbox.nonceLength);
|
||||||
|
|
||||||
|
const data_bytes = secretbox.open(cipher_bytes, nonce, key_bytes);
|
||||||
|
// Failed to decrypt message. Could be because the nonce, key, or ciphertext is invalid
|
||||||
|
// Ref https://github.com/dchest/tweetnacl-js/blob/master/test/04-secretbox.quick.js
|
||||||
|
// Ref https://github.com/dchest/tweetnacl-js/wiki/Examples#secretbox
|
||||||
|
if(!data_bytes) return null;
|
||||||
|
|
||||||
|
return data_bytes.toString("utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
export { make_key, encrypt, decrypt };
|
73
src/lib/io/StreamHelpers.mjs
Normal file
73
src/lib/io/StreamHelpers.mjs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
"use strict";
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A pair of functions to make writing to streams in Node.js less painful.
|
||||||
|
* I've used these in a number of different projects so far, and they both
|
||||||
|
* *appear* to be stable.
|
||||||
|
* @licence MPL-2.0
|
||||||
|
*
|
||||||
|
* Changelog
|
||||||
|
*********************************
|
||||||
|
* 22nd March 2021
|
||||||
|
* We should have brought this into our code snippet library *ages* ago lol :P
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes data to a stream, automatically waiting for the drain event if asked.
|
||||||
|
* @param {stream.Writable} stream_out The writable stream to write to.
|
||||||
|
* @param {string|Buffer|Uint8Array} data The data to write.
|
||||||
|
* @return {Promise} A promise that resolves when writing is complete.
|
||||||
|
*/
|
||||||
|
function write_safe(stream_out, data) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
// console.log(`Beginning write`);
|
||||||
|
// Handle errors
|
||||||
|
let handler_error = (error) => {
|
||||||
|
stream_out.off("error", handler_error);
|
||||||
|
// console.log(`Error received, handler detached, rejecting`);
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
stream_out.on("error", handler_error);
|
||||||
|
|
||||||
|
let returnval = typeof data == "string" ? stream_out.write(data, "utf-8") : stream_out.write(data);
|
||||||
|
// console.log(`Write returned`, returnval);
|
||||||
|
if(returnval) {
|
||||||
|
// We're good to go
|
||||||
|
stream_out.off("error", handler_error);
|
||||||
|
// console.log("We're good to go, handler detached, resolving");
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We need to wait for the drain event before continuing
|
||||||
|
// console.log(`Waiting for drain event`);
|
||||||
|
stream_out.once("drain", () => {
|
||||||
|
stream_out.off("error", handler_error);
|
||||||
|
// console.log(`Drain event received, handler detached, resolving`);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the given stream to end and finish writing data.
|
||||||
|
* @param {stream.Writable} stream The stream to end.
|
||||||
|
* @param {Buffer|string} [chunk=undefined] Optional. A chunk to write when calling .end().
|
||||||
|
* @return {Promise} A Promise that resolves when writing is complete.
|
||||||
|
*/
|
||||||
|
function end_safe(stream) {
|
||||||
|
return new Promise((resolve, _reject) => {
|
||||||
|
// Handle streams that have already been closed
|
||||||
|
if(stream.writableFinished) resolve();
|
||||||
|
stream.once("finish", resolve);
|
||||||
|
stream.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
write_safe, end_safe
|
||||||
|
};
|
70
src/lib/transport/Connection.mjs
Normal file
70
src/lib/transport/Connection.mjs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import net from 'net';
|
||||||
|
import { EventEmitter, once } from 'events';
|
||||||
|
|
||||||
|
import l from 'log';
|
||||||
|
import nexline from 'nexline';
|
||||||
|
|
||||||
|
import settings from '../../settings.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a connection to a single endpoint.
|
||||||
|
*/
|
||||||
|
class Connection extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to a peer and initialises a secure TCP connection thereto.
|
||||||
|
* @param {string} address The address to connect to.
|
||||||
|
* @param {string} port The TCP port to connect to.
|
||||||
|
* @return {net.Socket} A socket setup for secure communication.
|
||||||
|
*/
|
||||||
|
async connect(address, port) {
|
||||||
|
this.address = address; this.port = port;
|
||||||
|
this.socket = new new.Socket();
|
||||||
|
this.socket.connect({
|
||||||
|
address, port
|
||||||
|
});
|
||||||
|
await once(this.socket, "connect");
|
||||||
|
this.socket.setKeepAlive(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// this.reader = nexline({
|
||||||
|
// input: this.socket
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// this.read_task = read_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.socket.destroy();
|
||||||
|
this.emit("destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
async read_loop() {
|
||||||
|
try {
|
||||||
|
for await (let line of this.reader) {
|
||||||
|
handle_message(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
l.warn(`Warning: Killing connection to ${this.address}:${this.port} after error: ${settings.cli.verbose ? error : error.message}`);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_message(text) {
|
||||||
|
let message = JSON.parse(text);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Connection;
|
34
src/lib/transport/starttls.mjs
Normal file
34
src/lib/transport/starttls.mjs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import { once } from 'events';
|
||||||
|
import tls from 'tls';
|
||||||
|
|
||||||
|
import { write_safe } from '../io/StreamHelpers.mjs';
|
||||||
|
import { encrypt, decrypt } from '../crypto/secretbox.mjs';
|
||||||
|
|
||||||
|
|
||||||
|
export default async function starttls(socket, secret_join, { cert: cert_ours, key: key_ours }, is_server = true) {
|
||||||
|
// 1: Encrypt our self-signed cert and send it to the peer
|
||||||
|
const cert_encrypted = encrypt(secret_join, cert_ours);
|
||||||
|
await write_safe(socket, Buffer.from(cert_encrypted, "base64"));
|
||||||
|
|
||||||
|
// 2: Receive our peer's certificate and decrypt it
|
||||||
|
let data_bytes = Buffer.from(await once(socket, "data")[0], "base64");
|
||||||
|
|
||||||
|
const cert_theirs = decrypt(secret_join, data_bytes.toString("base64"));
|
||||||
|
if(cert_theirs === null) {
|
||||||
|
socket.destroy();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3: Upgrade to proper TLS
|
||||||
|
const tls_socket = new tls.TLSSocket(socket, {
|
||||||
|
isServer: is_server,
|
||||||
|
requestCert: true,
|
||||||
|
ca: cert_theirs,
|
||||||
|
cert: cert_ours,
|
||||||
|
key: key_ours
|
||||||
|
});
|
||||||
|
|
||||||
|
return tls_socket;
|
||||||
|
}
|
Loading…
Reference in a new issue