diff --git a/.gitignore b/.gitignore index 118c938..7c8d1e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules package-lock.json config.json *.pem +*.crt diff --git a/index.js b/index.js index cc4961f..0b0c004 100644 --- a/index.js +++ b/index.js @@ -2,8 +2,10 @@ const libp2p = require("libp2p") const TCP = require("libp2p-tcp") +const WS = require("libp2p-websockets") const Peer = require("peer-info") const Id = require("peer-id") +const multiaddr = require("multiaddr") const SPDY = require('libp2p-spdy') const MULTIPLEX = require('libp2p-multiplex') @@ -22,9 +24,20 @@ map(require("./test/ids.json"), Id.createFromJSON, (e, ids) => { listen.forEach(addr => peer.multiaddrs.add(addr)) + let tcp = new TCP() + let ws = new WS() + let l = [] + const create = tcp.createListener.bind(tcp) + tcp.createListener = (options, handler) => { + let n = create(options, handler) + n.handler = handler + l.push(n) + return n + } + const swarm = new libp2p({ transport: [ - new TCP() + tcp ], connection: { muxer: [ @@ -44,6 +57,16 @@ map(require("./test/ids.json"), Id.createFromJSON, (e, ids) => { swarm.start(err => { if (err) throw err - swarm.nodetrust.enable(console.log) + swarm.nodetrust.enable(err => { + if (err) throw err + let wss = ws.createListener({ + cert: swarm.nodetrust.chain, + key: swarm.nodetrust.key + }, l[0].handler) + wss.listen(multiaddr("/ip4/0.0.0.0/tcp/5285/ws"), err => { + if (err) throw err + console.log("Online @ https://localhost:5285") + }) + }) }) }) diff --git a/package.json b/package.json index cd27e39..263f29b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "libp2p-secio": "^0.8.1", "libp2p-spdy": "^0.11.0", "libp2p-tcp": "^0.11.1", - "libp2p-websockets": "^0.10.4" + "libp2p-websockets": "github:libp2p/js-libp2p-websockets#pass-options" }, "dependencies": { "node-forge": "^0.7.1", diff --git a/server/genca.sh b/server/genca.sh index 254136d..f21d99c 100644 --- a/server/genca.sh +++ b/server/genca.sh @@ -1,3 +1,8 @@ #!/bin/sh -openssl req -subj '/' -new -nodes -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem +openssl req -subj '/C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org' -new -nodes -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem +openssl x509 -in cacert.pem -inform PEM -out ca.crt + +# to install ca +# sudo cp ca.crt /usr/share/ca-certificates/extra/nodetrust.crt +# sudo dpkg-reconfigure ca-certificates diff --git a/server/src/ca/forge.js b/server/src/ca/forge.js index 663ecf7..d80fbed 100644 --- a/server/src/ca/forge.js +++ b/server/src/ca/forge.js @@ -32,7 +32,7 @@ module.exports = class ForgeCA { if (cn != cn_req) return cb(new Error("Rejecting request: commonName (" + cn + ") and requested commonName (" + cn_req + ") do not match!")) const cert = pki.createCertificate() - cert.serialNumber = '02' + cert.serialNumber = "03" cert.validity.notBefore = new Date() cert.validity.notAfter = new Date() @@ -48,6 +48,8 @@ module.exports = class ForgeCA { log('signing csr for %s', cn) - return cb(null, Buffer.from(pki.certificateToPem(cert))) + const pemout = Buffer.from(pki.certificateToPem(cert)) + + return cb(null, pemout, Buffer.concat([pemout, Buffer.from(this.cert)])) } } diff --git a/server/src/ca/index.js b/server/src/ca/index.js index 09db85d..9baec14 100644 --- a/server/src/ca/index.js +++ b/server/src/ca/index.js @@ -29,11 +29,12 @@ module.exports = (swarm, config) => { log('incomming certificate request from', pi.id.toB58String()) id.pubKey.verify(data.certRequest, data.signature, (err, ok) => { if (err || !ok) return cb(err) - ca.doCertRequest(data.certRequest, id, pi.id.toB58String() + "." + swarm.zone, data.signature, (err, certificate) => { + ca.doCertRequest(data.certRequest, id, swarm.getCN(id), data.signature, (err, certificate, fullchain) => { if (err) return cb(err) return respond({ success: true, - certificate + certificate, + fullchain }) }) }) diff --git a/server/src/config.dev.json b/server/src/config.dev.json index 165f5f2..78877d9 100644 --- a/server/src/config.dev.json +++ b/server/src/config.dev.json @@ -1,5 +1,5 @@ { - "zone": "node.libp2p.io", + "zone": "node.libp2p", "id": { "id": "QmRQuY14GoeyDx5DoFWq9xnCteSz6pWFKcopvJspei5LXa", "privKey": "CAASqQkwggSlAgEAAoIBAQDXW+yHJxrvsOVh6EqNowW0CNMZLvDX8Ai+Sy3zDf8LmgOtZaGvQW/5A07peETcYf/pBCw8p2eqZwzouVxXbPteYnGbLX5AV8edzb98PDIRFAb7tInRbzWcaIC6Bwh5hjiUC4smf2ZEuZl0St9zDGYFGYEPam6HSvIph1RfDPbMXU6X+AIqMXKiArc5K43BpEblNht432xR9ywDQUkJuoXM1NkERCjsE0VkO29MZ2IFM9yUp02JqGKNeubgwOsbfmbXsg715aiWoTiT6lTBJ5bgtydE56YO5jaKVuHsQw9u/QoqdygK6N9mK9vK/KANuveLOpQI9tXchHYwV1aY7VdFAgMBAAECggEBAKMTDoZBDFqYHvoGhf2+hmEPlZoqO2God1ZxYzf7Tzefxk0U+lvVN07ePHD6C1q+MqRl7oF3Wj5kjhfj1JK3CZks/k29Iy7hyVwrImaWxmq8OZV73ihjB7uKPn+fN8Gd61Xfb90U94Hu8M5oq89YgiA2cc4Uj+GO1NSxjyfyjyer0z/nOkQRCFgwh5NKxm8rxWHfDdwn3ftjwZglx9cRj3pwZ6g04Cga7HiQMEgZ3LRmwRVHKancqkqfT5woTgzR+8SpGJ4wu+8QVBcHPUWj91Rmt3jm3cLdH4/BBAIIY1RE23k6srWJw7axZOLOJKwgRJDM+qHr4LkYTAn89CoTggECgYEA/s2HqUK9O3S8+0Gf9oUM61GPQBqo8mzEjkeiXF1n2pKr+M0xavtCwj2fWrShpOiIKdXUyM3ANJya+aBDQ4/FPvOjFDGSR0ToQttcWdM+noXq+x6C4yiN/8U533mUKhlG+XzosH7PIaASDVLgKUV57GmbUIEuMEXHruhTHoh+OUECgYEA2F7zvFBPnk+yQGDkYbtrT5RpuqK9qU55ujLO2QuleL7Jm0hvK+/z3cTpuR6Lfn7nPcTl27vCxMLSJvLC/sBEuTcY02u+7PvclD9iBGVdvsXL9NoWBw5xYBXyBOXnQGQKcLhWSlDI4REHWezlcfkZ3Zicx8AqHB4nxaEMbkFG+QUCgYEAnkIHzaBPKluSeThEY3g2Ev6AS9+DKbdWycxCUr4NIBvTRmAkHn9a8owVqt/gOi3XTKysUeBBTiwqsXrR7GeiqjvnAUbcxPlOjR/0FzJ2hT1GOpyzzOVGdSMJk/zOgutaQuFLITxR6F+kdrQP2HJ3jNf3CKSDjKX6pW9VGPTL+UECgYEAg/HbVoHvG45kGAg+HS8qcHwDwbGOURmu95IWO5tzi99kmBIi4TtRjnKPSLlMvZXrs+pHdajZTB22A9RUfv+GqR/lPsBczK2GRM0mG6Io+bYq+ySm/CSdlMetL7l3PPgEudpfnLAI397/iaICBW+vi0eOi+0ugLkO7eCY8P9TpXUCgYAC87u6cHeNecWIjBEsZHoadOYXZTpQIZsVE2c1apcMFp8+xu89Jl4TM4iHYzuzpBuW9q/X7ZYAE35zUpR1iOE1YbWH3nXYj1JCYTuhFS+7acJ4bNfs7qy6AKCbqiw2fv9SNpY0gMn8Y/vLx6FQdzxU2AE8URANgSTA8pA9DaqVEQ==", diff --git a/server/src/dns/index.js b/server/src/dns/index.js index dd1f006..250d400 100644 --- a/server/src/dns/index.js +++ b/server/src/dns/index.js @@ -37,7 +37,7 @@ module.exports = (swarm, config) => { if (err || !ok) return cb(err) conn.getObservedAddrs((err, addr) => { if (err) return cb(err) - const dns = id.toB58String() + "." + swarm.zone + "." + const dns = swarm.getCN(id) + "." const ips = addr.map(addr => addr.toString()).filter(addr => addr.startsWith("/ip")).map(addr => { const s = addr.split("/") return { diff --git a/server/src/index.js b/server/src/index.js index 1945891..0f5ac31 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -11,6 +11,8 @@ const SPDY = require('libp2p-spdy') const MULTIPLEX = require('libp2p-multiplex') const SECIO = require('libp2p-secio') +const protos = require('./protos') + module.exports = function NodetrustServer(config) { const self = this @@ -39,6 +41,10 @@ module.exports = function NodetrustServer(config) { }, peer) swarm.zone = config.zone + swarm.getCN = id => { + if (id.toB58String) id = id.toB58String() + return protos.buildCN(id, swarm.zone) + } require("./ca")(swarm, config.ca) require("./dns")(swarm, config.dns) diff --git a/server/src/protos.js b/server/src/protos.js index ae52098..9c0e982 100644 --- a/server/src/protos.js +++ b/server/src/protos.js @@ -8,7 +8,7 @@ const once = require('once') module.exports = { info: protobuf('message Request { } message Result { required string zone = 1; }'), - ca: protobuf('message Request { required bytes certRequest = 1; required bytes signature = 2; } message Result { required bool success = 1; bytes certificate = 2; }'), + ca: protobuf('message Request { required bytes certRequest = 1; required bytes signature = 2; } message Result { required bool success = 1; bytes certificate = 2; bytes fullchain = 3; }'), dns: protobuf('message Request { required int64 time = 1; required bytes signature = 2; } message Result { required bool success = 1; }'), discovery: protobuf('message Request { required int32 numPeers = 1; repeated bytes multiaddr = 2; } message Peer { required string id = 1; repeated bytes multiaddr = 2; } message Result { required bool success = 1; repeated Peer peers = 2; }'), server: (conn, def, cb) => { @@ -46,5 +46,12 @@ module.exports = { cb(null, res) }) ) + }, + buildCN: (id, zone) => { + id = id.replace(/([A-Z])/g, c => c.toLowerCase() + "-").split("") + let n = [] + while (id.length) + n.push(id.splice(0, 60).join("")) + return n.concat([zone]).join(".") } } diff --git a/src/index.js b/src/index.js index 53fbffb..312c4c7 100644 --- a/src/index.js +++ b/src/index.js @@ -51,13 +51,18 @@ module.exports = class NodeTrust { log('enabling') this.getInfo(err => { if (err) return cb(err) - this.getCert((err, cert) => { + this.getCert((err, cert, key, chain) => { if (err) return cb(err) this.cert = cert + this.chain = chain + this.key = key + if (process.env.NODETRUST_LOG_KEYS) + console.log(chain.toString() + key.toString()) this.loop(err => { if (err) return cb(err) this.interval = setInterval(this.loop.bind(this), 5 * 60 * 1000 - 20000).unref() this.enabled = true + cb() }) }) }) @@ -101,11 +106,14 @@ module.exports = class NodeTrust { getCert(cb) { log('getting certificate') - this._getCertRequest(this.info, (err, request) => { + this._getCertRequest(this.info, (err, request, key) => { if (err) return cb(err) this.id.privKey.sign(request, (err, sign) => { if (err) return cb(err, sign) - this._getCert(request, sign, cb) + this._getCert(request, sign, (err, cert, chain) => { + if (err) return cb(err) + cb(null, cert, key, chain) + }) }) }) } @@ -118,7 +126,7 @@ module.exports = class NodeTrust { }, (err, res) => { if (err) return cb(err) if (!res.success || !res.certificate || !res.certificate.length) return cb(new Error('Server did not complete certificate request')) - cb(null, res.certificate) + cb(null, res.certificate, res.fullchain) }) }) } @@ -128,7 +136,7 @@ module.exports = class NodeTrust { csr.publicKey = keys.publicKey csr.setSubject([{ name: 'commonName', - value: this.id.toB58String() + "." + info.zone + value: protos.buildCN(this.id.toB58String(), info.zone) }, { name: 'countryName', value: 'US' @@ -170,7 +178,7 @@ module.exports = class NodeTrust { }] }])*/ csr.sign(keys.privateKey) - return cb(null, Buffer.from(forge.pki.certificationRequestToPem(csr))) + return cb(null, Buffer.from(forge.pki.certificationRequestToPem(csr)), Buffer.from(forge.pki.privateKeyToPem(keys.privateKey))) } // DNS