diff --git a/data/dbs/file/db.js b/data/dbs/file/db.js index d0df685..95f1fb7 100644 --- a/data/dbs/file/db.js +++ b/data/dbs/file/db.js @@ -87,6 +87,110 @@ definitions.push( ); +definitions.push( + { + "name": "Toyota", + "model": { + "@events": { + "door_open": { + "type": "object", + "fields": { + "timestamp": { + "description": "The time of event", + "type": "numeric" + }, + } + }, + }, + "@properties": { + "speed": { + "type": "object", + "fields": { + "unit": { + "description": "The unit identifier in KmH or MpH", + "type": "string" + }, + "value": { + "description": "The speed value", + "type": "numeric" + } + } + }, + "location": { + "type": "object", + "fields": { + "latitude": { + "description": "The latitude part of the location", + "type": "string" + }, + "longitude": { + "description": "The longitude part of the location", + "type": "numeric" + } + } + } + }, + "@actions": { + "unlock": null, + "lock": null + } + } + } +); + + +definitions.push( + { + "name": "Ford", + "model": { + "@events": { + "door_open": { + "type": "object", + "fields": { + "timestamp": { + "description": "The time of event", + "type": "numeric" + }, + } + }, + }, + "@properties": { + "speed": { + "type": "object", + "fields": { + "unit": { + "description": "The unit identifier in KmH or MpH", + "type": "string" + }, + "value": { + "description": "The speed value", + "type": "numeric" + } + } + }, + "location": { + "type": "object", + "fields": { + "latitude": { + "description": "The latitude part of the location", + "type": "string" + }, + "longitude": { + "description": "The longitude part of the location", + "type": "numeric" + } + } + } + }, + "@actions": { + "unlock": null, + "lock": null + } + } + } +); + + exports.find_thing = function find_thing(name, callback) { var thing = null; for (i = 0; i < definitions.length; i++) { diff --git a/examples/p2p_demo/config.js b/examples/p2p_demo/config.js index c039b95..5ea78c7 100644 --- a/examples/p2p_demo/config.js +++ b/examples/p2p_demo/config.js @@ -19,8 +19,20 @@ var config = { }, servers: { p2p: { - address: 'localhost', - port: 31300 + nodes: [ + { + address: 'localhost', + port: 31300, + nick: "wotseed01", + seeds: [] + }, + { + address: 'localhost', + port: 31301, + nick: "wotseed02", + seeds: [{ address: 'localhost', port: 31300 }] + } + ] } }, // The application database configuration. The ./data/dbs directory includes the database implementations diff --git a/examples/p2p_demo/demo.js b/examples/p2p_demo/demo.js index b01fb03..d1f0ff8 100644 --- a/examples/p2p_demo/demo.js +++ b/examples/p2p_demo/demo.js @@ -1,201 +1,193 @@ // set this global config variable first global.appconfig = require('./config'); -// define what things will be handled by this demo -global.is_door12_defined = true; -global.is_switch12_defined = true; - var events = require("events"); var logger = require('../../logger'); var db = require('../../data/db')(); var wot = require('../../framework'); -var eventh = require('../../libs/events/thingevents'); -//var simulator = require('./simulator'); +var uuid = require('uuid'); +var PeerNetwork = require('../../libs/transport/p2p/wotkad/peer_network'); +var simulator = require('./simulator'); +var config = global.appconfig; + +var peernet = new PeerNetwork(); var device = function (thing_name) { - var self = this; + var self = this; + this.name = thing_name; self.property_get = function (property, callback) { - logger.debug("get property from CoAP device: " + property ); - var msg = { - type: 'property_get', - name: thing_name, - property: property - }; + logger.debug("get property from the P2P network: " + property); - adapter.send({ host: self.host, port: self.port }, msg, function (err, result) { - if (err) { - return callback(err); - } + if (!this.node) { + return callback("peer node is not initialised"); + } - if (result && result.property && result.property == property && result.hasOwnProperty('value')) { - callback(null, result.value); - } - else { - callback("Invalid CoAP property_get response"); - } - }); + var key = thing_name + "/property/" + property; + this.node.get(key, callback); } self.setProperty = function (property, value) { logger.debug("send patch to device: " + property + ", value " + value); - var msg = { - type: 'patch', - name: thing_name, - property: property, - value: value - }; + var key = thing_name + "/patch/" + property; - adapter.send({ host: self.host, port: self.port }, msg, function (err, result) { - // TODO handles the result - }); + // TODO } self.action = function (action) { logger.debug("invoke action " + action + " at device simulator"); - var msg = { - type: 'action', - name: thing_name, - action: action - }; + var key = thing_name + "/action/" + action; - adapter.send({ host: self.host, port: self.port }, msg, function (err, result) { - // TODO handles the result - }); + // TODO + } + + self.is_msg_fordevice = function (keymsg) { + var elements = keymsg.split("/"); + if (elements&& elements[0] == this.name ) { + return {result: true, type: elements[1], value: elements[2] }; + } + + return false; } - // create the CoAP adapter - self.init = function(callback) { - db.find_adapter(thing_name, "coap", function (err, data) { + // create the P2P peer node + self.init = function ( model, address, port, callback) { + this.model = model; + + var seedaddr = config.servers.p2p.nodes[0].address; + var seedport = config.servers.p2p.nodes[0].port; + options = { + address: address, + port: port, + nick: uuid.v4(), + alg: {}, + private_key: {}, + public_key: {}, + seeds: [{ address: seedaddr, port: seedport }] + }; + this.node = peernet.create_peer(options); + + this.node.on('connect', function (err, value) { if (err) { - return callback(err); + return logger.error("peer connect error %j", err, {}); } - //start the CoAP client/server - if (!data || !data.device || !data.protocol || !data.host) { - return callback("Invalid adapter configuration data"); - } - - self.host = data.host; - self.port = data.port; + logger.debug("peer " + self.name + " %j connected to overlay network", value, {}); - adapter.init(data, function (err) { - callback(err); + peernet.on('data', function (key) { + var keyres = self.is_msg_fordevice(key); + if (keyres && keyres.result == true) { + self.node.get(key, function (err, value) { + if (err) { + return logger.error("peer get error %j", err, {}); + } + + logger.debug(self.name + ' P2P update type: ' + keyres.type + ', ' + keyres.value + ' value is : ' + value); + }); + } }); - + }); + + callback(); } self.unbind = function (callback) { - adapter.unbind(function (err) { - callback(err); - }); + // TODO remove the node from the overlay network + callback(); } - return self; - + return self; }; // // The implementations of the things // -var door_device = new device("door12"); -var switch_device = new device("switch12"); +var toyota_car = new device("Toyota"); +var ford_car = new device("Ford"); var things = [ { "thing": function (callback) { - db.find_thing("door12", callback); + db.find_thing("Toyota", callback); }, "implementation": { start: function (thing) { - door_device.init(function (err) { + toyota_car.init(thing.model, '127.0.0.1', 65520, function (err) { if (err) { - return logger.error("CoAP door12 adapter initialisation error: " + err); + return logger.error("P2P thing Toyota initialisation error: " + err); } }); }, stop: function (thing) { - door_device.unbind(function (err) { + toyota_car.unbind(function (err) { if (err) { - return logger.error("CoAP adapter unbind error: " + err); + return logger.error("P2P thing Toyota unbind error: " + err); } }); }, property_get: function (property, callback) { - door_device.property_get(property, function (err, value) { + toyota_car.property_get(property, function (err, value) { if (err) { callback(err); - return logger.error("CoAP adapter door12 " + property + " property_get error: " + err); + return logger.error("P2P thing Toyota " + property + " property_get error: " + err); } + logger.debug('peer Toyota thing received key: ' + key + ' value: ' + value); + callback(null, value); }); - }, - // must be the property set handler implemented here otherwise - // the client is unable to set the property - patch: function (thing, property, value) { - door_device.setProperty(property, value); - }, - unlock: function (thing) { - logger.info('at implementation ' + thing.name + ' "unlock action invoked -> call the device'); - door_device.action('unlock'); - }, - lock: function (thing) { - logger.info('at implementation ' + thing.name + ' "lock" action invoked -> call the device'); - door_device.action('lock'); } } }, { "thing": function (callback) { - db.find_thing("switch12", callback); - }, + db.find_thing("Ford", callback); + }, "implementation": { start: function (thing) { - switch_device.init(function (err) { + ford_car.init(thing.model, '127.0.0.1', 65521, function (err) { if (err) { - return logger.error("CoAP switch12 adapter initialisation error: " + err); + return logger.error("P2P thing Ford initialisation error: " + err); } }); }, - stop: function (thing) { - switch_device.unbind(function (err) { + stop: function (thing) { + ford_car.unbind(function (err) { if (err) { - return logger.error("CoAP adapter unbind error: " + err); + return logger.error("P2P thing Ford unbind error: " + err); } }); }, property_get: function (property, callback) { - switch_device.property_get(property, function (err, value) { + ford_car.property_get(property, function (err, value) { if (err) { callback(err); - return logger.error("CoAP adapter switch12 " + property + " property_get error: " + err); + return logger.error("P2P thing Ford " + property + " property_get error: " + err); } + logger.debug('peer Ford thing received key: ' + key + ' value: ' + value); + callback(null, value); }); - }, - patch: function (thing, property, value) { - switch_device.setProperty(property, value); } } - } + } ]; // call the framework initialisation method and pass an array of things definitions to the framework // for this demo the things are defined here try { - logger.debug("Calling framework init()"); - wot.init(things); + logger.debug("Initialising framework"); + wot.transport_init(); + wot.things_init(things); + // start the device device simulator + simulator.start(); } catch (e) { logger.error("Error in initialising framework " + e.message); } - -// start the device P2P simulator -//simulator.start(); diff --git a/examples/p2p_demo/kaddht_demo.js b/examples/p2p_demo/kaddht_demo.js index 605c49b..10ca8f6 100644 --- a/examples/p2p_demo/kaddht_demo.js +++ b/examples/p2p_demo/kaddht_demo.js @@ -52,16 +52,6 @@ function onPut(err) { return log.error("onPut error %j", err, {}); } - //node1.get('door12/events/bell', function (err, value) { - // if (err) { - // return log.error("error %j", err); - // } - // log.debug('----------------------------------------------------'); - // log.debug('node1 received door12/events/bell value is : ' + value); - // log.debug('----------------------------------------------------'); - - //}); - node2.get('door12/events/bell', function (err, value) { if (err) { return log.error("error %j", err, {}); @@ -72,14 +62,6 @@ function onPut(err) { addnode(); - //if (!is_modified) { - // // now modify the data - // is_modified = true; - // node2.put('door12/events/bell', 'beep2 from node2', onPut); - //} - //else { - // //putloop(); - //} }); } diff --git a/examples/p2p_demo/peer_demo.js b/examples/p2p_demo/peer_demo.js index f3e1b5f..d6d7597 100644 --- a/examples/p2p_demo/peer_demo.js +++ b/examples/p2p_demo/peer_demo.js @@ -1,51 +1,87 @@ var PeerNetwork = require('../../libs/transport/p2p/wotkad/peer_network'); var logger = require('../../logger'); +var peernet = new PeerNetwork(); -var peers = new PeerNetwork(); - +// create the overlay network with node1 var options = { + address: '127.0.0.1', + port: 65529, + nick: 'seed1', + alg: {}, + private_key: {}, + public_key: {} +}; +var seed1 = peernet.create_peer(options); + +options = { address: '127.0.0.1', port: 65530, - nick: 'node1' + nick: 'seed2', + alg: {}, + private_key: {}, + public_key: {}, + seeds: [{ address: '127.0.0.1', port: 65529 }] }; -var peer1 = peers.create_peer(options); +var seed2 = peernet.create_peer(options); +// connect to the overlay network options = { address: '127.0.0.1', port: 65531, nick: 'node2', - seeds: [{ address: '127.0.0.1', port: 65530 }], + alg: {}, + private_key: {}, + public_key: {}, + seeds: [{ address: '127.0.0.1', port: 65529 }] }; -var peer2 = peers.create_peer(options); +var peer2 = peernet.create_peer(options); options = { address: '127.0.0.1', port: 65532, nick: 'node3', - seeds: [{ address: '127.0.0.1', port: 65530 }], + alg: {}, + private_key: {}, + public_key: {}, + seeds: [{ address: '127.0.0.1', port: 65530 }] }; -var peer3 = peers.create_peer(options); +var peer3 = peernet.create_peer(options); -peer3.on('connect', function (err, value) { +peer2.on('connect', function (err, value) { if (err) { - return logger.error("peer connect error %j", err, {}); + return logger.error("peer connect error %j", err, {}); } - - logger.debug("peer %j connected to overlay network", value, {}); + + logger.debug("peer peer2 %j connected to overlay network", value, {}); peer2.put('door12/events/bell', 'beep1 from peer2', function (err) { if (err) { return logger.error("onPut error %j", err, {}); } + + }); + +}); + +peer3.on('connect', function (err, value) { + if (err) { + return logger.error("peer connect error %j", err, {}); + } - peer3.get('door12/events/bell', function (err, value) { - if (err) { - return logger.error("error %j", err, {}); - } - logger.debug('--------------------------------------------------------------------------------------------------------'); - logger.debug('peer3 received door12/events/bell value is : ' + value); - logger.debug('--------------------------------------------------------------------------------------------------------'); - }); + logger.debug("peer peer3 %j connected to overlay network", value, {}); + + peernet.on('data', function (key) { + if (key == 'door12/events/bell') { + peer3.get('door12/events/bell', function (err, value) { + if (err) { + return logger.error("peer get error %j", err, {}); + } + logger.debug('--------------------------------------------------------------------------------------------------------'); + logger.debug('peer3 received door12/events/bell value is : ' + value); + logger.debug('--------------------------------------------------------------------------------------------------------'); + }); + } }); + }); \ No newline at end of file diff --git a/examples/p2p_demo/simulator.js b/examples/p2p_demo/simulator.js new file mode 100644 index 0000000..f6c8ce2 --- /dev/null +++ b/examples/p2p_demo/simulator.js @@ -0,0 +1,123 @@ +var logger = require('../../logger'); +var PeerNetwork = require('../../libs/transport/p2p/wotkad/peer_network'); +var config = global.appconfig; + +var peernet = new PeerNetwork(); + +var device_node = function (thing, address, port) { + var self = this; + this.name = thing.name; + + logger.debug("starting P2P device simulator for " + this.name); + + + + var seedaddr = config.servers.p2p.nodes[0].address; + var seedport = config.servers.p2p.nodes[0].port; + options = { + address: address, + port: port, + nick: this.name, + alg: {}, + private_key: {}, + public_key: {}, + seeds: [{ address: seedaddr, port: seedport }] + }; + this.node = peernet.create_peer(options); + + this.put = function(prop, value) { + var key = thing.name + prop; + this.node.put(key, value, function (err) { + if (err) { + return logger.error("device_node put error %j", err, {}); + } + }); + } + + return self; +} + +var toyota_device = {}; +var toyota_prop_values = {}; +toyota_prop_values["speed"] = 0; +toyota_prop_values["location"] = 0; + +var car1 = { + "name": "Toyota", + "model": { + "events": { + + }, + // for patch include the writable properties from the data/dbs/file/db.js file + "properties": { + "get": function (property) { + return toyota_prop_values[property]; + }, + "speed": function (value){ + var setspeedval = function () { + var speed = 50; + speed += Math.floor(Math.random() * 15); + + //send to the WoT P2P network + toyota_device.put("/property/speed", speed); + + toyota_prop_values["speed"] = speed; + }; + setInterval(setspeedval, 5000); + }, + "location": function () { + }, + }, + "actions": { + + } + } +}; + + +var ford_device = {}; +var ford_prop_values = {}; +ford_prop_values["speed"] = 0; +ford_prop_values["location"] = 0; + +var car2 = { + "name": "Ford", + "model": { + "events": { + + }, + // for patch include the writable properties from the data/dbs/file/db.js file + "properties": { + "get": function (property) { + return ford_prop_values[property]; + }, + "speed": function (value) { + var setspeedval = function () { + var speed = 70; + speed += Math.floor(Math.random() * 15); + + //send to the WoT P2P network + ford_device.put("/property/speed", speed); + + ford_prop_values["speed"] = speed; + }; + setInterval(setspeedval, 6000); + }, + "location": function () { + }, + }, + "actions": { + + } + } +}; + + +exports.start = function start() { + logger.debug('Start device simulator to communicate with WoT via a P2P network'); + toyota_device = new device_node(car1, 'localhost', 60000); + car1.model.properties.speed(); + + ford_device = new device_node(car2, 'localhost', 60001); + car2.model.properties.speed(); +} diff --git a/framework.js b/framework.js index 3817236..583608f 100644 --- a/framework.js +++ b/framework.js @@ -13,4 +13,20 @@ exports.init = function init(things) { logger.debug("WoT Framework is initialised"); -} \ No newline at end of file +} + +exports.things_init = function things_init(things) { + // call the thing handler to start handling the things + thing_handler.init(things); + + logger.debug("WoT Framework things are initialised"); +} + + +exports.transport_init = function transport_init() { + // start the servers + server_handler.init(); + + logger.debug("WoT Framework transport is initialised"); + +} diff --git a/libs/crypto/crypto_handler.js b/libs/crypto/crypto_handler.js new file mode 100644 index 0000000..4ffddd1 --- /dev/null +++ b/libs/crypto/crypto_handler.js @@ -0,0 +1,21 @@ + +function CryptoHandler(alg, initstr) { + if (!(this instanceof CryptoHandler)) { + return new CryptoHandler(); + } + + this.alg = alg; +} + +CryptoHandler.prototype.sign = function () { + var self = this; + +} + + +CryptoHandler.prototype.verify = function () { + +} + + +module.exports = CryptoHandler; \ No newline at end of file diff --git a/libs/crypto/ecc/EccDsa.js b/libs/crypto/ecc/EccDsa.js new file mode 100644 index 0000000..d4ac86c --- /dev/null +++ b/libs/crypto/ecc/EccDsa.js @@ -0,0 +1,337 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + +var crypto = require('crypto'); +var assert = require('assert'); + +var ecurve = require('./ecurvelib/index'); +var ECPointFp = ecurve.ECPointFp; +var BigInteger = require('bigi'); + +module.exports = (function () { + var _ecparams = ecurve.getECParams('secp256k1'); + var methods = { + hmacSHA256: hmacSHA256, + calcPubKeyRecoveryParam: calcPubKeyRecoveryParam, deterministicGenerateK: deterministicGenerateK, + recoverPubKey: recoverPubKey, sign: sign, verify: verify, verifyRaw: verifyRaw + } + + function attachECParams(ecdsa, ecparams) { + Object.keys(methods).forEach(function (method) { + ecdsa[method] = methods[method].bind(null, ecparams); + }) + } + + function _ecdsa(curveName) { + if (!curveName) + var ecparams = ecurve.getECParams('secp256k1') + else + var ecparams = ecurve.getECParams(curveName) + + var ecdsa = {} + //attach other non-ecparams functions + Object.keys(_ecdsa).forEach(function (k) { + ecdsa[k] = _ecdsa[k] + }) + + ecdsa.ecparams = ecparams + attachECParams(ecdsa, ecparams) + + return ecdsa + } + + //attach ecparams + _ecdsa.ecparams = _ecparams + + //attach functions + _ecdsa.parseSig = parseSig + _ecdsa.parseSigCompact = parseSigCompact + _ecdsa.serializeSig = serializeSig + _ecdsa.serializeSigCompact = serializeSigCompact + + attachECParams(_ecdsa, _ecparams) + + return _ecdsa +})(); + + + +/** + * Calculate pubkey extraction parameter. + * + * When extracting a pubkey from a signature, we have to + * distinguish four different cases. Rather than putting this + * burden on the verifier, Bitcoin includes a 2-bit value with the + * signature. + * + * This function simply tries all four cases and returns the value + * that resulted in a successful pubkey recovery. + */ +function hmacSHA256(v, k) { + return crypto.createHmac('sha256', k).update(v).digest() +} + +function calcPubKeyRecoveryParam(ecparams, e, signature, Q) { + for (var i = 0; i < 4; i++) { + var Qprime = recoverPubKey(ecparams, e, signature, i) + + if (Qprime.equals(Q)) { + return i + } + } + + throw new Error('Unable to find valid recovery factor') +} + +function deterministicGenerateK(ecparams, hash, D) { + assert(Buffer.isBuffer(hash), 'Hash must be a Buffer, not ' + hash) + assert.equal(hash.length, 32, 'Hash must be 256 bit') + assert(BigInteger.isBigInteger(D, true), 'Private key must be a BigInteger') + + var x = D.toBuffer(32); + var k = new Buffer(32); + var v = new Buffer(32); + k.fill(0); + v.fill(1); + + k = hmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k); + v = hmacSHA256(v, k); + + k = hmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k); + v = hmacSHA256(v, k); + v = hmacSHA256(v, k); + + var n = ecparams.n; + var kB = BigInteger.fromBuffer(v).mod(n); + assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value'); + assert(kB.compareTo(ecparams.n) < 0, 'Invalid k value'); + + return kB; +} + +function parseSig(buffer) { + assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence') + assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length') + + assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer') + var rLen = buffer.readUInt8(3) + var rB = buffer.slice(4, 4 + rLen) + + var offset = 4 + rLen + assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)') + var sLen = buffer.readUInt8(1 + offset) + var sB = buffer.slice(2 + offset) + offset += 2 + sLen + + assert.equal(offset, buffer.length, 'Invalid DER encoding') + + return { r: BigInteger.fromDERInteger(rB), s: BigInteger.fromDERInteger(sB) } +} + +function parseSigCompact(buffer) { + assert.equal(buffer.length, 65, 'Invalid signature length') + var i = buffer.readUInt8(0) - 27 + + // At most 3 bits + assert.equal(i, i & 7, 'Invalid signature parameter') + var compressed = !!(i & 4) + + // Recovery param only + i = i & 3 + + var r = BigInteger.fromBuffer(buffer.slice(1, 33)) + var s = BigInteger.fromBuffer(buffer.slice(33)) + + return { + signature: { + r: r, + s: s + }, + i: i, + compressed: compressed + } +} + +/** + * Recover a public key from a signature. + * + * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public + * Key Recovery Operation". + * + * http://www.secg.org/download/aid-780/sec1-v2.pdf + */ +function recoverPubKey(ecparams, e, signature, i) { + assert.strictEqual(i & 3, i, 'The recovery param is more than two bits') + + var r = signature.r + var s = signature.s + + // A set LSB signifies that the y-coordinate is odd + // By reduction, the y-coordinate is even if it is clear + var isYEven = !(i & 1) + + // The more significant bit specifies whether we should use the + // first or second candidate key. + var isSecondKey = i >> 1 + + var n = ecparams.n + var G = ecparams.g + var curve = ecparams.curve + var p = curve.q + var a = curve.a.toBigInteger() + var b = curve.b.toBigInteger() + + // We precalculate (p + 1) / 4 where p is the field order + if (!curve.P_OVER_FOUR) { + curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2) + } + + // 1.1 Compute x + var x = isSecondKey ? r.add(n) : r + + // 1.3 Convert x to point + var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p) + var beta = alpha.modPow(curve.P_OVER_FOUR, p) + + // If beta is even, but y isn't, or vice versa, then convert it, + // otherwise we're done and y == beta. + var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta + + // 1.4 Check that nR isn't at infinity + var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + R.validate() + + // 1.5 Compute -e from e + var eNeg = e.negate().mod(n) + + // 1.6 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + var rInv = r.modInverse(n) + + var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv) + Q.validate() + + if (!verifyRaw(ecparams, e, signature, Q)) { + throw new Error("Pubkey recovery unsuccessful") + } + + return Q +} + +function serializeSig(signature) { + //var rBa = r.toByteArraySigned(); + //var sBa = s.toByteArraySigned(); + var rBa = signature.r.toDERInteger() + var sBa = signature.s.toDERInteger() + + + var sequence = []; + sequence.push(0x02); // INTEGER + sequence.push(rBa.length); + sequence = sequence.concat(rBa); + + sequence.push(0x02); // INTEGER + sequence.push(sBa.length); + sequence = sequence.concat(sBa); + + sequence.unshift(sequence.length); + sequence.unshift(0x30); // SEQUENCE + + return sequence; +} + +function serializeSigCompact(signature, i, compressed) { + if (compressed) { + i += 4 + } + + i += 27 + + var buffer = new Buffer(65) + buffer.writeUInt8(i, 0) + + signature.r.toBuffer(32).copy(buffer, 1) + signature.s.toBuffer(32).copy(buffer, 33) + + return buffer +} + +function sign(ecparams, hash, privateKey) { + if (Buffer.isBuffer(privateKey)) + var D = BigInteger.fromBuffer(privateKey) + else + var D = privateKey//big integer for legacy compatiblity + + var k = deterministicGenerateK(ecparams, hash, D) + + var n = ecparams.n + var G = ecparams.g + var Q = G.multiply(k) + var e = BigInteger.fromBuffer(hash) + + var r = Q.getX().toBigInteger().mod(n) + assert.notEqual(r.signum(), 0, 'Invalid R value') + + var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n) + assert.notEqual(s.signum(), 0, 'Invalid S value') + + var N_OVER_TWO = n.shiftRight(1) + + // enforce low S values, see bip62: 'low s values in signatures' + if (s.compareTo(N_OVER_TWO) > 0) { + s = n.subtract(s) + } + + return { r: r, s: s } +} + +function verify(ecparams, hash, signature, pubkey) { + assert(signature.r && signature.s, "Invalid signature.") + + var Q; + if (Buffer.isBuffer(pubkey)) { + Q = ECPointFp.decodeFrom(ecparams.curve, pubkey); + } else { + throw new Error("Invalid format for pubkey value, must be Buffer"); + } + var e = BigInteger.fromBuffer(hash); + + return verifyRaw(ecparams, e, { r: signature.r, s: signature.s }, Q); +} + +function verifyRaw(ecparams, e, signature, Q) { + var n = ecparams.n + var G = ecparams.g + + var r = signature.r + var s = signature.s + + if (r.signum() === 0 || r.compareTo(n) >= 0) return false + if (s.signum() === 0 || s.compareTo(n) >= 0) return false + + var c = s.modInverse(n) + + var u1 = e.multiply(c).mod(n) + var u2 = r.multiply(c).mod(n) + + var point = G.multiplyTwo(u1, Q, u2) + var v = point.getX().toBigInteger().mod(n) + + return v.equals(r) +} \ No newline at end of file diff --git a/libs/crypto/ecc/EccKey.js b/libs/crypto/ecc/EccKey.js new file mode 100644 index 0000000..3d84fda --- /dev/null +++ b/libs/crypto/ecc/EccKey.js @@ -0,0 +1,161 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var crypto = require('crypto'); +var ecurve = require('ecurve'); +var ecparams = ecurve.getCurveByName('secp256k1'); +var BigInteger = require('bigi'); +var ks = require('./KeyString'); +var ecdsa = require('./EccDsa'); + +function EccKey(bytes, compressed) { + if (!(this instanceof EccKey)) { + return new EccKey(bytes, compressed) + } + + if (typeof compressed == 'boolean') + this._compressed = compressed + else + this._compressed = true + + if (bytes) + this.privateKey = bytes +} + + +Object.defineProperty(EccKey.prototype, 'privateKey', { + enumerable: true, configurable: true, + get: function () { + return this.key + }, + set: function (bytes) { + if (typeof bytes != 'string') { + throw new Error("The private key input must be a string"); + } + + var byteArr; + var hash = crypto.createHash('sha256').update(bytes).digest(); + if (Buffer.isBuffer(hash)) { + this.key = hash; + byteArr = [].slice.call(hash) + } + else { + throw new Error('Error generating private key hash'); + } + + if (this.key.length != 32) + throw new Error("private key bytes must have a length of 32") + + //_exportKey => privateKey + (0x01 if compressed) + if (this._compressed) + this._exportKey = Buffer.concat([ this.key, new Buffer([0x01]) ]) + else + this._exportKey = Buffer.concat([ this.key ]) //clone key as opposed to passing a reference (relevant to Node.js only) + + this.keyBigInteger = BigInteger.fromByteArrayUnsigned(byteArr) + + //reset + this._publicPoint = null + this._pubKeyHash = null + } +}) + +Object.defineProperty(EccKey.prototype, 'privateExportKey', { + get: function () { + return this._exportKey + } +}) + + +Object.defineProperty(EccKey.prototype, 'pubKeyHash', { + get: function () { + if (this._pubKeyHash) return this._pubKeyHash + this._pubKeyHash = crypto.createHash('rmd160').update(this.publicKey).digest() + return this._pubKeyHash + } +}) + +Object.defineProperty(EccKey.prototype, 'publicKey', { + get: function () { + return new Buffer(this.publicPoint.getEncoded(this.compressed)) + } +}) + +Object.defineProperty(EccKey.prototype, 'publicPoint', { + get: function () { + if (!this._publicPoint) { + this._publicPoint = ecparams.G.multiply(this.keyBigInteger) + } + return this._publicPoint + } +}) + +Object.defineProperty(EccKey.prototype, 'compressed', { + get: function () { + return this._compressed + }, + set: function (val) { + var c = !!val + if (c === this._compressed) return + + //reset key stuff + var pk = this.privateKey + this._compressed = c + this.privateKey = pk + } +}) + +Object.defineProperty(EccKey.prototype, 'publicKeyStr', { + get: function () { + return this.publicKey.toString('hex') + } +}) + +Object.defineProperty(EccKey.prototype, 'pubKeyHashStr', { + get: function () { + return this.pubKeyHash.toString('hex') + } +}) + +Object.defineProperty(EccKey.prototype, 'pubKeyEncode', { + get: function () { + return ks.encode(this.publicKey); + } +}) + + +EccKey.prototype.toString = function (format) { + return this.privateKey.toString('hex') +} + +EccKey.prototype.sign = function sign(text, account) { + var buffer = new Buffer(text, "utf-8"); + var hash = crypto.createHash('sha256').update(buffer).digest(); + var signbuffer = ecdsa.sign(hash, this.privateKey); + var ser1 = ecdsa.serializeSig(signbuffer); + var ser2 = new Buffer(ser1); + var signatureb64 = ser2.toString('base64'); + var hashstr = hash.toString('hex'); + var encodedKey = this.pubKeyEncode; + return { hash: hashstr, signature: signatureb64, account: account, pkchecksum: encodedKey.checksum }; +} + +module.exports = EccKey; + diff --git a/libs/crypto/ecc/EccVerify.js b/libs/crypto/ecc/EccVerify.js new file mode 100644 index 0000000..f8e5aa7 --- /dev/null +++ b/libs/crypto/ecc/EccVerify.js @@ -0,0 +1,70 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var ecdsa = require('./EccDsa'); +var ks = require('./KeyString'); + +function EccVerify(base64Pk, checksum, hash, signature, pk_checksum) { + if (!base64Pk || !checksum) { + throw new Error("Invalid contract verify parameters"); + return; + } + if (!hash) { + throw new Error("Invalid transaction hash buffer"); + return; + } + if (!signature) { + throw new Error("Invalid transaction signature buffer"); + return; + } + if (!pk_checksum) { + throw new Error("Invalid transaction public key checksum buffer"); + return; + } + + if (checksum != pk_checksum) { + throw new Error("Public key checksums do not match "); + return; + } + + var msghash = new Buffer(hash, 'hex'); + + //var signBuffer = new Buffer(signature, 'hex'); + var signBuffer = null; + try { + signBuffer = new Buffer(signature, 'base64'); + } + catch (e) { + signBuffer = null; + } + if (!signBuffer) { + throw new Error("Invalid signature buffer."); + return; + } + + var signature = ecdsa.parseSig(signBuffer); + + var publickey = ks.decode(base64Pk, checksum); + var valid = ecdsa.verify(msghash, signature, publickey); + return valid; +} + +module.exports = EccVerify; + diff --git a/libs/crypto/ecc/KeyString.js b/libs/crypto/ecc/KeyString.js new file mode 100644 index 0000000..0220d42 --- /dev/null +++ b/libs/crypto/ecc/KeyString.js @@ -0,0 +1,75 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var crypto = require('crypto'); +var assert = require('assert'); + +function encode(payload) { + if (!payload || !Buffer.isBuffer(payload)) { + throw new Error("Invalid KeyString encode paramater"); + } + var chksum = sha256x2(payload).slice(0, 4); + var result = { + key: payload.toString('hex'), + checksum: chksum.toString('hex') + }; + return result; +} + +function decode(hexkey, checksum) { + if (!hexkey || !checksum || hexkey.constructor != String || checksum.constructor != String) { + throw new Error("Invalid KeyString decode paramater"); + } + + var buffer, newChecksum; + try { + buffer = new Buffer(hexkey, 'hex'); + newChecksum = sha256x2(buffer).slice(0, 4) + } + catch (err) { + throw new Error('Encoding key failed. Error: ' + err); + } + + if (checksum !== newChecksum.toString('hex')) + throw new Error('Invalid checksum') + + return buffer; +} + +function isValid(hexkey, checksum) { + try { + decode(hexkey, checksum) + } catch (e) { + return false + } + return true; +} + + +function sha256x2(buffer) { + var sha = crypto.createHash('sha256').update(buffer).digest() + return crypto.createHash('sha256').update(sha).digest() +} + +module.exports = { + encode: encode, + decode: decode, + isValid: isValid +} \ No newline at end of file diff --git a/libs/crypto/ecc/ecurvelib/curve.js b/libs/crypto/ecc/ecurvelib/curve.js new file mode 100644 index 0000000..54977cf --- /dev/null +++ b/libs/crypto/ecc/ecurvelib/curve.js @@ -0,0 +1,51 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var BigInteger = require('bigi') + +var ECFieldElementFp = require('./field-element') +var ECPointFp = require('./point') + +module.exports = ECCurveFp + +function ECCurveFp(q,a,b) { + this._q = q; + this._a = this.fromBigInteger(a); + this._b = this.fromBigInteger(b); + this.infinity = new ECPointFp(this, null, null); +}; + +Object.defineProperty(ECCurveFp.prototype, 'q', {get: function() { return this._q}}) +Object.defineProperty(ECCurveFp.prototype, 'a', {get: function() { return this._a}}) +Object.defineProperty(ECCurveFp.prototype, 'b', {get: function() { return this._b}}) + +ECCurveFp.prototype.equals = function(other) { + if(other == this) return true; + return(this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b)); +}; + +ECCurveFp.prototype.getInfinity = function() { + return this.infinity; +}; + +ECCurveFp.prototype.fromBigInteger = function(x) { + return new ECFieldElementFp(this.q, x); +}; + diff --git a/libs/crypto/ecc/ecurvelib/field-element.js b/libs/crypto/ecc/ecurvelib/field-element.js new file mode 100644 index 0000000..cf12a9d --- /dev/null +++ b/libs/crypto/ecc/ecurvelib/field-element.js @@ -0,0 +1,65 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + + +module.exports = ECFieldElementFp + +function ECFieldElementFp(q,x) { + this.x = x; + // TODO if(x.compareTo(q) >= 0) error + this.q = q; +}; + +ECFieldElementFp.prototype.equals = function(other) { + if(other == this) return true; + return (this.q.equals(other.q) && this.x.equals(other.x)); +}; + +ECFieldElementFp.prototype.toBigInteger = function() { + return this.x; +}; + +ECFieldElementFp.prototype.negate = function() { + return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)); +}; + +ECFieldElementFp.prototype.add = function(b) { + return new ECFieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q)); +}; + +ECFieldElementFp.prototype.subtract = function(b) { + return new ECFieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q)); +}; + +ECFieldElementFp.prototype.multiply = function(b) { + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q)); +}; + +ECFieldElementFp.prototype.square = function() { + return new ECFieldElementFp(this.q, this.x.square().mod(this.q)); +}; + +ECFieldElementFp.prototype.divide = function feFpDivide(b) { + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q)); +}; + +ECFieldElementFp.prototype.getByteLength = function () { + return Math.floor((this.toBigInteger().bitLength() + 7) / 8); +}; \ No newline at end of file diff --git a/libs/crypto/ecc/ecurvelib/index.js b/libs/crypto/ecc/ecurvelib/index.js new file mode 100644 index 0000000..a6cd2ee --- /dev/null +++ b/libs/crypto/ecc/ecurvelib/index.js @@ -0,0 +1,35 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var ECFieldElementFp = require('./field-element') +var ECPointFp = require('./point') +var ECCurveFp = require('./curve') +var getECParams = require('./names') + +//for legacy compatibility, remove in the future +ECCurveFp.ECPointFp = ECPointFp + +module.exports = { + ECCurveFp: ECCurveFp, + ECFieldElementFp: ECFieldElementFp, + ECPointFp: ECPointFp, + getECParams: getECParams +} + diff --git a/libs/crypto/ecc/ecurvelib/names.js b/libs/crypto/ecc/ecurvelib/names.js new file mode 100644 index 0000000..641208e --- /dev/null +++ b/libs/crypto/ecc/ecurvelib/names.js @@ -0,0 +1,213 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var BigInteger = require('bigi'); + +var ECCurveFp = require('./curve'); +var ECPointFp = require('./point') + + +// Named EC curves + +// ---------------- +// X9ECParameters + +// constructor +function X9ECParameters(curve,g,n,h) { + this._curve = curve; + this._g = g; + this._n = n; + this._h = h; +} + +Object.defineProperty(X9ECParameters.prototype, 'curve', {get: function() { return this._curve }}) +Object.defineProperty(X9ECParameters.prototype, 'g', {get: function() { return this._g }}) +Object.defineProperty(X9ECParameters.prototype, 'n', {get: function() { return this._n }}) +Object.defineProperty(X9ECParameters.prototype, 'h', {get: function() { return this._h }}) + + +// ---------------- +// SECNamedCurves + +function fromHex(s) { return new BigInteger(s, 16); } + +var namedCurves = { + secp128r1: function() { + // p = 2^128 - 2^97 - 1 + var p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("E87579C11079F43DD824993C2CEE5ED3"); + //byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679"); + var n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "161FF7528B899B2D0C28607CA52C5B86" + + "CF5AC8395BAFEB13C02DA292DDED7A83");*/ + + var x = BigInteger.fromHex("161FF7528B899B2D0C28607CA52C5B86") + var y = BigInteger.fromHex("CF5AC8395BAFEB13C02DA292DDED7A83") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp160k1: function() { + // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); + var a = BigInteger.ZERO; + var b = fromHex("7"); + //byte[] S = null; + var n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + + "938CF935318FDCED6BC28286531733C3F03C4FEE");*/ + + var x = BigInteger.fromHex("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB") + var y = BigInteger.fromHex("938CF935318FDCED6BC28286531733C3F03C4FEE") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp160r1: function() { + // p = 2^160 - 2^31 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"); + var b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"); + //byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345"); + var n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "4A96B5688EF573284664698968C38BB913CBFC82" + + "23A628553168947D59DCC912042351377AC5FB32");*/ + + var x = BigInteger.fromHex("4A96B5688EF573284664698968C38BB913CBFC82") + var y = BigInteger.fromHex("23A628553168947D59DCC912042351377AC5FB32") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp192k1: function() { + // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); + var a = BigInteger.ZERO; + var b = fromHex("3"); + //byte[] S = null; + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");*/ + + var x = BigInteger.fromHex("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D") + var y = BigInteger.fromHex("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp192r1: function() { + // p = 2^192 - 2^64 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"); + var b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"); + //byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + + var x = BigInteger.fromHex("188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012") + var y = BigInteger.fromHex("07192B95FFC8DA78631011ED6B24CDD573F977A11E794811") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp224r1: function() { + // p = 2^224 - 2^96 + 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"); + var b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"); + //byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");*/ + + var x = BigInteger.fromHex("B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21") + var y = BigInteger.fromHex("BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp256k1: function() { + // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); + var a = BigInteger.ZERO; + var b = fromHex("7"); + //byte[] S = null; + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");*/ + + var x = BigInteger.fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798") + var y = BigInteger.fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return new X9ECParameters(curve, G, n, h); + }, + + secp256r1: function() { + // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1 + var p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); + //byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); + var n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + /*var G = curve.decodePointHex("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");*/ + + var x = BigInteger.fromHex("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296") + var y = BigInteger.fromHex("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5") + var G = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + + return new X9ECParameters(curve, G, n, h); + } +} + +module.exports = function getSECCurveByName(name) { + return (typeof namedCurves[name] == 'function')? namedCurves[name]() : null; +} diff --git a/libs/crypto/ecc/ecurvelib/point.js b/libs/crypto/ecc/ecurvelib/point.js new file mode 100644 index 0000000..893fe1f --- /dev/null +++ b/libs/crypto/ecc/ecurvelib/point.js @@ -0,0 +1,334 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + + +var assert = require('assert') + +var BigInteger = require('bigi') + +module.exports = ECPointFp + + +function ECPointFp(curve,x,y,z) { + this.curve = curve; + this.x = x; + this.y = y; + // Projective coordinates: either zinv == null or z * zinv == 1 + // z and zinv are just BigIntegers, not fieldElements + if(z == null) { + this.z = BigInteger.ONE; + } + else { + this.z = z; + } + + this.zinv = null; + this.compressed = true +}; + +ECPointFp.prototype.getX = function() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q)); +}; + +ECPointFp.prototype.getY = function() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q)); +}; + +ECPointFp.prototype.equals = function(other) { + if(other == this) return true; + if(this.isInfinity()) return other.isInfinity(); + if(other.isInfinity()) return this.isInfinity(); + var u, v; + // u = Y2 * Z1 - Y1 * Z2 + u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q); + if(!u.equals(BigInteger.ZERO)) return false; + // v = X2 * Z1 - X1 * Z2 + v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q); + return v.equals(BigInteger.ZERO); +}; + +ECPointFp.prototype.isInfinity = function() { + if((this.x == null) && (this.y == null)) return true; + return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO); +}; + +ECPointFp.prototype.negate = function() { + return new ECPointFp(this.curve, this.x, this.y.negate(), this.z); +}; + +ECPointFp.prototype.add = function(b) { + if(this.isInfinity()) return b; + if(b.isInfinity()) return this; + + // u = Y2 * Z1 - Y1 * Z2 + var u = b.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(b.z)).mod(this.curve.q); + // v = X2 * Z1 - X1 * Z2 + var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q); + + if(BigInteger.ZERO.equals(v)) { + if(BigInteger.ZERO.equals(u)) { + return this.twice(); // this == b, so double + } + return this.curve.getInfinity(); // this = -b, so infinity + } + + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + var x2 = b.x.toBigInteger(); + var y2 = b.y.toBigInteger(); + + var v2 = v.square(); + var v3 = v2.multiply(v); + var x1v2 = x1.multiply(v2); + var zu2 = u.square().multiply(this.z); + + // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) + var x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.curve.q); + // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 + var y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(b.z).add(u.multiply(v3)).mod(this.curve.q); + // z3 = v^3 * z1 * z2 + var z3 = v3.multiply(this.z).multiply(b.z).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +}; + +ECPointFp.prototype.twice = function() { + if(this.isInfinity()) return this; + if(this.y.toBigInteger().signum() == 0) return this.curve.getInfinity(); + + // TODO: optimized handling of constants + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + + var y1z1 = y1.multiply(this.z); + var y1sqz1 = y1z1.multiply(y1).mod(this.curve.q); + var a = this.curve.a.toBigInteger(); + + // w = 3 * x1^2 + a * z1^2 + var w = x1.square().multiply(THREE); + if(!BigInteger.ZERO.equals(a)) { + w = w.add(this.z.square().multiply(a)); + } + w = w.mod(this.curve.q); + // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) + var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q); + // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 + var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.square().multiply(w)).mod(this.curve.q); + // z3 = 8 * (y1 * z1)^3 + var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +}; + +// Simple NAF (Non-Adjacent Form) multiplication algorithm +// TODO: modularize the multiplication algorithm +ECPointFp.prototype.multiply = function(k) { + if(this.isInfinity()) return this; + if(k.signum() == 0) return this.curve.getInfinity(); + + var e = k; + var h = e.multiply(new BigInteger("3")); + + var neg = this.negate(); + var R = this; + + var i; + for(i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + + var hBit = h.testBit(i); + var eBit = e.testBit(i); + + if (hBit != eBit) { + R = R.add(hBit ? this : neg); + } + } + + return R; +}; + +// Compute this*j + x*k (simultaneous multiplication) +ECPointFp.prototype.multiplyTwo = function(j,x,k) { + var i; + if(j.bitLength() > k.bitLength()) { + i = j.bitLength() - 1; + } else { + i = k.bitLength() - 1; + } + + var R = this.curve.getInfinity(); + var both = this.add(x); + while(i >= 0) { + R = R.twice(); + if(j.testBit(i)) { + if(k.testBit(i)) { + R = R.add(both); + } else { + R = R.add(this); + } + } else { + if(k.testBit(i)) { + R = R.add(x); + } + } + --i; + } + + return R; +}; + +ECPointFp.prototype.getEncoded = function(doCompress) { + if (this.isInfinity()) return [0]; // Infinity point encoded is simply '00' + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + + var compressed = this.compressed + if (typeof doCompress == 'boolean') + compressed = doCompress + + // Determine size of q in bytes + var byteLength = Math.floor((this.curve.q.bitLength() + 7) / 8); + + // Get value as a 32-byte Buffer + // Fixed length based on a patch by bitaddress.org and Casascius + var enc = [].slice.call(x.toBuffer(byteLength)) + + if (compressed) { + if (y.isEven()) { + // Compressed even pubkey + // M = 02 || X + enc.unshift(0x02); + } else { + // Compressed uneven pubkey + // M = 03 || X + enc.unshift(0x03); + } + } else { + // Uncompressed pubkey + // M = 04 || X || Y + enc.unshift(0x04); + enc = enc.concat([].slice.call(y.toBuffer(byteLength))) + } + return new Buffer(enc); +}; + +ECPointFp.decodeFrom = function(curve, buffer) { + var type = buffer.readUInt8(0); + var compressed = (type !== 4) + var x = BigInteger.fromBuffer(buffer.slice(1, 33)) + var y = null + + if (compressed) { + assert.equal(buffer.length, 33, 'Invalid sequence length') + assert(type === 0x02 || type === 0x03, 'Invalid sequence tag') + + var isYEven = (type === 0x02) + var a = curve.a.toBigInteger() + var b = curve.b.toBigInteger() + var p = curve.q + + // We precalculate (p + 1) / 4 where p is the field order + if (!curve.P_OVER_FOUR) { + curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2) + } + + // Convert x to point + var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p) + var beta = alpha.modPow(curve.P_OVER_FOUR, p) + + // If beta is even, but y isn't, or vice versa, then convert it, + // otherwise we're done and y == beta. + y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta + + } else { + assert.equal(buffer.length, 65, 'Invalid sequence length') + + y = BigInteger.fromBuffer(buffer.slice(33)) + } + + var pt = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); + pt.compressed = compressed + return pt +}; + +ECPointFp.prototype.isOnCurve = function () { + if (this.isInfinity()) return true; + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + var a = this.curve.a.toBigInteger(); + var b = this.curve.b.toBigInteger(); + var n = this.curve.q; + var lhs = y.multiply(y).mod(n); + var rhs = x.multiply(x).multiply(x) + .add(a.multiply(x)).add(b).mod(n); + return lhs.equals(rhs); +}; + +ECPointFp.prototype.toString = function () { + if (this.isInfinity()) return '(INFINITY)'; + return '('+this.getX().toBigInteger().toString()+','+ + this.getY().toBigInteger().toString()+')'; +}; + +/** + * Validate an elliptic curve point. + * + * See SEC 1, section 3.2.2.1: Elliptic Curve Public Key Validation Primitive + */ +ECPointFp.prototype.validate = function () { + var n = this.curve.q; + + // Check Q != O + if (this.isInfinity()) { + throw new Error("Point is at infinity."); + } + + // Check coordinate bounds + var x = this.getX().toBigInteger(); + var y = this.getY().toBigInteger(); + if (x.compareTo(BigInteger.ONE) < 0 || + x.compareTo(n.subtract(BigInteger.ONE)) > 0) { + throw new Error('x coordinate out of bounds'); + } + if (y.compareTo(BigInteger.ONE) < 0 || + y.compareTo(n.subtract(BigInteger.ONE)) > 0) { + throw new Error('y coordinate out of bounds'); + } + + // Check y^2 = x^3 + ax + b (mod n) + if (!this.isOnCurve()) { + throw new Error("Point is not on the curve."); + } + + // Check nQ = 0 (Q is a scalar multiple of G) + if (this.multiply(n).isInfinity()) { + // TODO: This check doesn't work - fix. + throw new Error("Point is not a scalar multiple of G."); + } + + return true; +}; \ No newline at end of file diff --git a/libs/crypto/ecc/readme.md b/libs/crypto/ecc/readme.md new file mode 100644 index 0000000..90448ea --- /dev/null +++ b/libs/crypto/ecc/readme.md @@ -0,0 +1,4 @@ +# WoT ECC crypto implementation. + +Based on source files from https://github.com/cryptocoinjs + diff --git a/libs/message/jose/jwt.js b/libs/message/jose/jwt.js new file mode 100644 index 0000000..e851ebf --- /dev/null +++ b/libs/message/jose/jwt.js @@ -0,0 +1,181 @@ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +https://github.com/hokaccha/node-jwt-simple/blob/master/lib/jwt.js + +Copyright (C) 2015 The W3C WoT Team + +*/ + + + +var crypto = require('crypto'); + + +/** + * support algorithm mapping + */ +var algorithmMap = { + ES256: 'ES256', + ES384: 'ES384', + ES512: 'ES512' +}; + + +/** + * expose object + */ +var jwt = module.exports; + + +/** + * version + */ +jwt.version = '0.2.0'; + +/** + * Decode jwt + * + * @param {Object} token + * @param {String} key + * @param {Boolean} noVerify + * @param {String} algorithm + * @return {Object} payload + * @api public + */ +jwt.decode = function jwt_decode(token, key, noVerify, algorithm) { + // check token + if (!token) { + throw new Error('No token supplied'); + } + // check segments + var segments = token.split('.'); + if (segments.length !== 3) { + throw new Error('Not enough or too many segments'); + } + + // All segment should be base64 + var headerSeg = segments[0]; + var payloadSeg = segments[1]; + var signatureSeg = segments[2]; + + // base64 decode and parse JSON + var header = JSON.parse(base64urlDecode(headerSeg)); + var payload = JSON.parse(base64urlDecode(payloadSeg)); + + if (!noVerify) { + var signingMethod = algorithmMap[algorithm || header.alg]; + if (!signingMethod ) { + throw new Error('Algorithm not supported'); + } + + // verify signature. `sign` will return base64 string. + var signingInput = [headerSeg, payloadSeg].join('.'); + if (!verify(signingInput, key, signingMethod, signingType, signatureSeg)) { + throw new Error('Signature verification failed'); + } + } + + return payload; +}; + + +/** + * Encode jwt + * + * @param {Object} payload + * @param {String} key + * @param {String} algorithm + * @return {String} token + * @api public + */ +jwt.encode = function jwt_encode(payload, key, algorithm) { + // Check key + if (!key) { + throw new Error('Require key'); + } + + // Check algorithm, default is HS256 + if (!algorithm) { + algorithm = 'HS256'; + } + + var signingMethod = algorithmMap[algorithm]; + if (!signingMethod ) { + throw new Error('Algorithm not supported'); + } + + // header, typ is fixed value. + var header = { typ: 'JWT', alg: algorithm }; + + // create segments, all segments should be base64 string + var segments = []; + segments.push(base64urlEncode(JSON.stringify(header))); + segments.push(base64urlEncode(JSON.stringify(payload))); + segments.push(sign(segments.join('.'), key, signingMethod, signingType)); + + return segments.join('.'); +}; + + +/** + * private util functions + */ + +function verify(input, key, method, type, signature) { + if (type === "hmac") { + return (signature === sign(input, key, method, type)); + } + else if (type == "sign") { + return crypto.createVerify(method) + .update(input) + .verify(key, base64urlUnescape(signature), 'base64'); + } + else { + throw new Error('Algorithm type not recognized'); + } +} + +function sign(input, key, method, type) { + var base64str; + if (type === "hmac") { + base64str = crypto.createHmac(method, key).update(input).digest('base64'); + } + else if (type == "sign") { + base64str = crypto.createSign(method).update(input).sign(key, 'base64'); + } + else { + throw new Error('Algorithm type not recognized'); + } + + return base64urlEscape(base64str); +} + +function base64urlDecode(str) { + return new Buffer(base64urlUnescape(str), 'base64').toString(); +} + +function base64urlUnescape(str) { + str += new Array(5 - str.length % 4).join('='); + return str.replace(/\-/g, '+').replace(/_/g, '/'); +} + +function base64urlEncode(str) { + return base64urlEscape(new Buffer(str).toString('base64')); +} + +function base64urlEscape(str) { + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); +} \ No newline at end of file diff --git a/libs/message/message.js b/libs/message/message.js index 5f28270..83ea39b 100644 --- a/libs/message/message.js +++ b/libs/message/message.js @@ -1 +1,46 @@ - \ No newline at end of file +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + +var assert = require('assert'); +var crypto = require('crypto'); + + +function WoTMessage(private_key, payload, options) { + if (!(this instanceof WoTMessage)) { + return new WoTMessage(private_key, payload, options); + } + + if (!options || !options.alg) { + // default algorithm is ECC + options.alg = 'ECC'; + } + + // currently only ECC is supported + assert(options.alg == "ECC", "In this version only elliptic curve cryptography algorithms supported"); + + // TODO create a json web token + return payload; +} + +WoTMessage.prototype.verify = function (public_key, message) { + + return true; +} + +module.exports = WoTMessage; diff --git a/libs/transport/p2p/wotkad/kaddht.js b/libs/transport/p2p/wotkad/kaddht.js index 552d294..ce1da68 100644 --- a/libs/transport/p2p/wotkad/kaddht.js +++ b/libs/transport/p2p/wotkad/kaddht.js @@ -1,5 +1,20 @@ -/** -* @module kad +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -15,25 +30,26 @@ var Node = require('./lib/node'); * @param {function} onConnect */ module.exports = function createNode(options, onConnect) { - if (options.seeds) { - assert(Array.isArray(options.seeds), 'Invalid `options.seeds` supplied'); - } else { - options.seeds = []; - } + if (options.seeds) { + assert(Array.isArray(options.seeds), 'Invalid `options.seeds` supplied'); + } + else { + options.seeds = []; + } - for (var i = 0; i < options.seeds.length; i++) { - var seed = options.seeds[i]; - } + for (var i = 0; i < options.seeds.length; i++) { + var seed = options.seeds[i]; + } - var node = new Node(options); + var node = new Node(options); - async.eachSeries(options.seeds, connectToSeed, onConnect); + async.eachSeries(options.seeds, connectToSeed, onConnect); - function connectToSeed(seed, done) { - node.connect(seed, done); - } + function connectToSeed(seed, done) { + node.connect(seed, done); + } - return node; + return node; }; module.exports.Bucket = require('./lib/bucket'); diff --git a/libs/transport/p2p/wotkad/lib/bucket.js b/libs/transport/p2p/wotkad/lib/bucket.js index 746e35f..401b100 100644 --- a/libs/transport/p2p/wotkad/lib/bucket.js +++ b/libs/transport/p2p/wotkad/lib/bucket.js @@ -1,7 +1,23 @@ -/** -* @module kad/bucket +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ + 'use strict'; var _ = require('lodash'); @@ -55,17 +71,17 @@ Bucket.prototype.getContact = function(index) { * @param {object} contact */ Bucket.prototype.addContact = function(contact) { - assert(contact instanceof Contact, 'Invalid contact supplied'); + assert(contact instanceof Contact, 'Invalid contact supplied'); - if (!this.hasContact(contact.nodeID)) { - var index = _.sortedIndex(this._contacts, contact, function(contact) { - return contact.lastSeen; - }); + if (!this.hasContact(contact.nodeID)) { + var index = _.sortedIndex(this._contacts, contact, function(contact) { + return contact.lastSeen; + }); - this._contacts.splice(index, 0, contact); - } + this._contacts.splice(index, 0, contact); + } - return this; + return this; }; /** @@ -104,15 +120,15 @@ Bucket.prototype.hasContact = function(nodeID) { * @param {object} contact */ Bucket.prototype.indexOf = function(contact) { - assert(contact instanceof Contact, 'Invalid contact supplied'); + assert(contact instanceof Contact, 'Invalid contact supplied'); - for (var i = 0; i < this.getSize(); i++) { - if (this.getContact(i).nodeID === contact.nodeID) { - return i; + for (var i = 0; i < this.getSize(); i++) { + if (this.getContact(i).nodeID === contact.nodeID) { + return i; + } } - } - return -1; + return -1; }; module.exports = Bucket; diff --git a/libs/transport/p2p/wotkad/lib/constants.js b/libs/transport/p2p/wotkad/lib/constants.js index 0758990..9774693 100644 --- a/libs/transport/p2p/wotkad/lib/constants.js +++ b/libs/transport/p2p/wotkad/lib/constants.js @@ -1,5 +1,20 @@ -/** -* @module kad/constants +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; diff --git a/libs/transport/p2p/wotkad/lib/contact.js b/libs/transport/p2p/wotkad/lib/contact.js index 854fcc2..f6ed02d 100644 --- a/libs/transport/p2p/wotkad/lib/contact.js +++ b/libs/transport/p2p/wotkad/lib/contact.js @@ -1,5 +1,20 @@ -/** -* @module kad/contact +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -14,18 +29,21 @@ var utils = require('./utils'); */ function Contact(options) { - assert(this instanceof Contact, 'Invalid instance was supplied'); - assert(options instanceof Object, 'Invalid options were supplied'); + assert(this instanceof Contact, 'Invalid instance was supplied'); + assert(options instanceof Object, 'Invalid options were supplied'); - Object.defineProperty(this, 'nodeID', { - value: options.nodeID || this._createNodeID(), - configurable: false, - enumerable: true - }); + Object.defineProperty( + this, + 'nodeID', { + value: options.nodeID || this._createNodeID(), + configurable: false, + enumerable: true + } + ); - assert(utils.isValidKey(this.nodeID), 'Invalid nodeID was supplied'); + assert(utils.isValidKey(this.nodeID), 'Invalid nodeID was supplied'); - this.seen(); + this.seen(); } /** @@ -33,7 +51,7 @@ function Contact(options) { * #seen */ Contact.prototype.seen = function() { - this.lastSeen = Date.now(); + this.lastSeen = Date.now(); }; /* istanbul ignore next */ @@ -42,7 +60,7 @@ Contact.prototype.seen = function() { * #_createNodeID */ Contact.prototype._createNodeID = function() { - throw new Error('Method not implemented'); + throw new Error('Method not implemented'); }; module.exports = Contact; diff --git a/libs/transport/p2p/wotkad/lib/item.js b/libs/transport/p2p/wotkad/lib/item.js index 1639c7e..110b2b2 100644 --- a/libs/transport/p2p/wotkad/lib/item.js +++ b/libs/transport/p2p/wotkad/lib/item.js @@ -1,5 +1,20 @@ -/** -* @module kad/item +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -7,6 +22,7 @@ var assert = require('assert'); var constants = require('./constants'); var utils = require('./utils'); +var crypto = require('crypto'); /** * Represents an item to store @@ -17,23 +33,29 @@ var utils = require('./utils'); * @param {number} timestamp - optional */ function Item(key, value, publisher, timestamp) { - if (!(this instanceof Item)) { - return new Item(key, value, publisher, timestamp); - } - - assert.ok(typeof key === 'string', 'Invalid key supplied'); - assert.ok(value, 'Invalid value supplied'); - assert(utils.isValidKey(publisher), 'Invalid publisher nodeID supplied'); - - if (timestamp) { - assert(typeof timestamp === 'number', 'Invalid timestamp supplied'); - assert(Date.now() >= timestamp, 'Timestamp cannot be in the future'); - } - - this.key = key; - this.value = value; - this.publisher = publisher; - this.timestamp = timestamp || Date.now(); + if (!(this instanceof Item)) { + return new Item(key, value, publisher, timestamp); + } + + assert.ok(typeof key === 'string', 'Invalid key supplied'); + assert.ok(value, 'Invalid value supplied'); + assert(utils.isValidKey(publisher), 'Invalid publisher nodeID supplied'); + + if (timestamp) { + assert(typeof timestamp === 'number', 'Invalid timestamp supplied'); + assert(Date.now() >= timestamp, 'Timestamp cannot be in the future'); + } + + var obj = [key, value]; + var str = JSON.stringify(obj); + var buffer = new Buffer(str); + var hashval = crypto.createHash('sha1').update(buffer).digest(); + this.hash = hashval.toString('hex'); + + this.key = key; + this.value = value; + this.publisher = publisher; + this.timestamp = timestamp || Date.now(); } module.exports = Item; diff --git a/libs/transport/p2p/wotkad/lib/message.js b/libs/transport/p2p/wotkad/lib/message.js index 19c4fb7..27f819a 100644 --- a/libs/transport/p2p/wotkad/lib/message.js +++ b/libs/transport/p2p/wotkad/lib/message.js @@ -1,5 +1,20 @@ -/** -* @module kad/message +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -17,15 +32,15 @@ var Contact = require('./contact'); * @param {object} fromContact */ function Message(type, params, fromContact) { - if (!(this instanceof Message)) { - return new Message(type, params, fromContact); - } + if (!(this instanceof Message)) { + return new Message(type, params, fromContact); + } - assert(constants.MESSAGE_TYPES.indexOf(type) !== -1, 'Invalid message type'); - assert(fromContact instanceof Contact, 'Invalid contact supplied'); + assert(constants.MESSAGE_TYPES.indexOf(type) !== -1, 'Invalid message type'); + assert(fromContact instanceof Contact, 'Invalid contact supplied'); - this.type = type; - this.params = merge(params, fromContact); + this.type = type; + this.params = merge(params, fromContact); } /** @@ -33,7 +48,7 @@ function Message(type, params, fromContact) { * #serialize */ Message.prototype.serialize = function() { - return new Buffer(JSON.stringify(this), 'utf8'); + return new Buffer(JSON.stringify(this), 'utf8'); }; module.exports = Message; diff --git a/libs/transport/p2p/wotkad/lib/node.js b/libs/transport/p2p/wotkad/lib/node.js index efad9dd..00d898d 100644 --- a/libs/transport/p2p/wotkad/lib/node.js +++ b/libs/transport/p2p/wotkad/lib/node.js @@ -1,5 +1,20 @@ -/** -* @module kademlia/node +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -81,9 +96,8 @@ Node.prototype.connect = function(options, callback) { async.waterfall( [ this._updateContact.bind(this, seed), - this._findNode.bind( - this, this._self.nodeID), - this._refreshBucketsBeyondClosest.bind(this) + this._findNode.bind(this, this._self.nodeID), + this._refreshBucketsBeyondClosest.bind(this) ], function (err) { if (err) { @@ -150,12 +164,18 @@ Node.prototype._putValidatedKeyValue = function(key, value, callback) { node._log.error('failed to find nodes - reason: %s', err.message); return callback(err); } + + //for (var i = 0; i < contacts.length; i++) { + // if (!(contacts[i] instanceof Contact)) { + // throw new Error('Invalid contact'); + // } + //} if (contacts.length === 0) { node._log.error('no contacts are available'); contacts = node._getNearestContacts(key, constants.K, node._self.nodeID); } - + //node._log.debug('found %d contacts for STORE operation', contacts.length); async.each(contacts, function (contact, done) { @@ -226,44 +246,47 @@ Node.prototype._startReplicationInterval = function() { * #_replicate */ Node.prototype._replicate = function() { - var self = this; - var stream = this._storage.createReadStream(); - - this._log.info('starting local database replication'); + var self = this; + var stream = this._storage.createReadStream(); - stream.on('data', function(data) { - if (typeof data.value === 'string') { - try { - data.value = JSON.parse(data.value); - } catch(err) { - return self._log.error('failed to parse value from %s', data.value); - } - } + this._log.info('starting local database replication'); - // if we are not the publisher, then replicate every T_REPLICATE - if (data.value.publisher !== self._self.nodeID) { - self.put(data.key, data.value.value, function(err) { - if (err) { - self._log.error('failed to replicate item at key %s', data.key); + stream.on('data', function(data) { + if (typeof data.value === 'string') { + try { + data.value = JSON.parse(data.value); + } + catch (err) { + return self._log.error('failed to parse value from %s', data.value); + } } - }); - // if we are the publisher, then only replicate every T_REPUBLISH - } else if (Date.now() <= data.value.timestamp + constants.T_REPUBLISH) { - self.put(data.key, data.value.value, function(err) { - if (err) { - self._log.error('failed to republish item at key %s', data.key); + + // if we are not the publisher, then replicate every T_REPLICATE + if (data.value.publisher !== self._self.nodeID) { + self.put(data.key, data.value.value, function(err) { + if (err) { + self._log.error('failed to replicate item at key %s', data.key); + } + }); + + // if we are the publisher, then only replicate every T_REPUBLISH + } + else if (Date.now() <= data.value.timestamp + constants.T_REPUBLISH) { + self.put(data.key, data.value.value, function(err) { + if (err) { + self._log.error('failed to republish item at key %s', data.key); + } + }); } - }); - } - }); + }); - stream.on('error', function(err) { - self._log.error('error while replicating: %s', err.message); - }); + stream.on('error', function(err) { + self._log.error('error while replicating: %s', err.message); + }); - stream.on('end', function() { - self._log.info('database replication complete'); - }); + stream.on('end', function() { + self._log.info('database replication complete'); + }); }; /** @@ -462,11 +485,11 @@ Node.prototype._updateContact = function(contact, callback) { * @param {object} params */ Node.prototype._handlePing = function(params) { - var contact = this._rpc._createContact(params); - var message = new Message('PONG', { referenceID: params.rpcID }, this._self); + var contact = this._rpc._createContact(params); + var message = new Message('PONG', { referenceID: params.rpcID }, this._self); - this._log.info('received PING from %s, sending PONG', params.nodeID); - this._rpc.send(contact, message); + this._log.info('received PING from %s, sending PONG', params.nodeID); + this._rpc.send(contact, message); }; /** @@ -509,13 +532,18 @@ Node.prototype._storeValidatedKeyValue = function (item, params) { this._storage.put(item.key, JSON.stringify(item), function(err) { var contact = node._rpc._createContact(params); - var message = new Message('STORE_REPLY', { - referenceID: params.rpcID, - success: !!err - }, node._self); + var message = new Message( + 'STORE_REPLY', { + referenceID: params.rpcID, + success: !!err + }, + node._self); //node._log.debug('successful store, notifying %s', params.nodeID); node._rpc.send(contact, message); + + // signal an event that the message was stored + node.emit('store', node._self.nodeID, item ); }); }; @@ -530,10 +558,14 @@ Node.prototype._handleFindNode = function(params) { var contact = this._rpc._createContact(params); var near = this._getNearestContacts(params.key, constants.K, params.nodeID); - var message = new Message('FIND_NODE_REPLY', { - referenceID: params.rpcID, - contacts: near - }, this._self); + + var message = new Message( + 'FIND_NODE_REPLY', + { + referenceID: params.rpcID, + contacts: near + }, + this._self); //this._log.debug('sending %s nearest %d contacts', params.nodeID, near.length, {}); @@ -583,66 +615,66 @@ Node.prototype._handleFindValue = function(params) { * @param {string} nodeID */ Node.prototype._getNearestContacts = function(key, limit, nodeID) { - var contacts = []; - var hashedKey = utils.createID(key); - var initialIndex = utils.getBucketIndex(this._self.nodeID, hashedKey); - var ascBucketIndex = initialIndex; - var descBucketIndex = initialIndex; - - if (this._buckets[initialIndex]) { - addNearestFromBucket(this._buckets[initialIndex]); - } + var contacts = []; + var hashedKey = utils.createID(key); + var initialIndex = utils.getBucketIndex(this._self.nodeID, hashedKey); + var ascBucketIndex = initialIndex; + var descBucketIndex = initialIndex; + + if (this._buckets[initialIndex]) { + addNearestFromBucket(this._buckets[initialIndex]); + } - while (contacts.length < limit && ascBucketIndex < constants.B) { - ascBucketIndex++; + while (contacts.length < limit && ascBucketIndex < constants.B) { + ascBucketIndex++; - if (this._buckets[ascBucketIndex]) { - addNearestFromBucket(this._buckets[ascBucketIndex]); + if (this._buckets[ascBucketIndex]) { + addNearestFromBucket(this._buckets[ascBucketIndex]); + } } - } - while (contacts.length < limit && descBucketIndex >= 0) { - descBucketIndex--; + while (contacts.length < limit && descBucketIndex >= 0) { + descBucketIndex--; - if (this._buckets[descBucketIndex]) { - addNearestFromBucket(this._buckets[descBucketIndex]); + if (this._buckets[descBucketIndex]) { + addNearestFromBucket(this._buckets[descBucketIndex]); + } } - } - function addToContacts(contact) { - var isContact = contact instanceof Contact; - var poolNotFull = contacts.length < limit; - var notRequester = contact.nodeID !== nodeID; + function addToContacts(contact) { + var isContact = contact instanceof Contact; + var poolNotFull = contacts.length < limit; + var notRequester = contact.nodeID !== nodeID; - if (isContact && poolNotFull && notRequester) { - contacts.push(contact); + if (isContact && poolNotFull && notRequester) { + contacts.push(contact); + } } - } - function addNearestFromBucket(bucket) { - var contactList = bucket.getContactList(); - var distances = contactList.map(addDistance).sort(sortKeysByDistance); - var howMany = limit - contacts.length; + function addNearestFromBucket(bucket) { + var contactList = bucket.getContactList(); + var distances = contactList.map(addDistance).sort(sortKeysByDistance); + var howMany = limit - contacts.length; - distances.splice(0, howMany).map(pluckContact).forEach(addToContacts); - } + distances.splice(0, howMany).map(pluckContact).forEach(addToContacts); + } - function pluckContact(c) { - return c.contact; - } + function pluckContact(c) { + return c.contact; + } - function sortKeysByDistance(a, b) { - return utils.compareKeys(a.distance, b.distance); - } + function sortKeysByDistance(a, b) { + return utils.compareKeys(a.distance, b.distance); + } - function addDistance(contact) { - return { - contact: contact, - distance: utils.getDistance(contact.nodeID, hashedKey) - }; - } + function addDistance(contact) { + return { + contact: contact, + distance: utils.getDistance(contact.nodeID, hashedKey) + }; + } - return contacts; + return contacts; }; module.exports = Node; diff --git a/libs/transport/p2p/wotkad/lib/router.js b/libs/transport/p2p/wotkad/lib/router.js index 8be2cb8..37d40a4 100644 --- a/libs/transport/p2p/wotkad/lib/router.js +++ b/libs/transport/p2p/wotkad/lib/router.js @@ -1,7 +1,21 @@ -/** -* @module kad/router -*/ +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi +Copyright (C) 2015 The W3C WoT Team + +*/ 'use strict'; var assert = require('assert'); @@ -33,6 +47,7 @@ function Router(type, key, node) { this.limit = constants.ALPHA; this.shortlist = node._getNearestContacts(key, this.limit, node._self.nodeID); + this.closestNode = this.shortlist[0]; this.previousClosestNode = null; this.contacted = {}; @@ -47,20 +62,23 @@ function Router(type, key, node) { * @param {function} callback */ Router.prototype.route = function(callback) { - if (!this.closestNode) { - return callback(new Error('Not connected to any peers')); - } + if (!this.closestNode) { + return callback(new Error('Not connected to any peers')); + } - this.closestNodeDistance = utils.getDistance( - this.hashedKey, - this.closestNode.nodeID - ); + this.closestNodeDistance = utils.getDistance( + this.hashedKey, + this.closestNode.nodeID + ); - this.message = new Message('FIND_' + this.type, { - key: this.key - }, this.node._self); + this.message = new Message( + 'FIND_' + this.type, + { + key: this.key + }, + this.node._self); - this._iterativeFind(this.shortlist, callback); + this._iterativeFind(this.shortlist, callback); }; /** @@ -70,11 +88,11 @@ Router.prototype.route = function(callback) { * @param {function} callback */ Router.prototype._iterativeFind = function(contacts, callback) { - var self = this; + var self = this; - async.each(contacts, this._queryContact.bind(this), function(err) { - self._handleQueryResults(callback); - }); + async.each(contacts, this._queryContact.bind(this), function(err) { + self._handleQueryResults(callback); + }); }; /** @@ -84,17 +102,17 @@ Router.prototype._iterativeFind = function(contacts, callback) { * @param {function} callback */ Router.prototype._queryContact = function(contactInfo, callback) { - var self = this; - var contact = this.node._rpc._createContact(contactInfo); + var self = this; + var contact = this.node._rpc._createContact(contactInfo); - this.node._rpc.send(contact, this.message, function(err, params) { - if (err) { - self._removeFromShortList(contact.nodeID); - return callback(); - } + this.node._rpc.send(contact, this.message, function(err, params) { + if (err) { + self._removeFromShortList(contact.nodeID); + return callback(); + } - self._handleFindResult(params, contact, callback); - }); + self._handleFindResult(params, contact, callback); + }); }; /** @@ -104,53 +122,55 @@ Router.prototype._queryContact = function(contactInfo, callback) { * @param {object} contact * @param {function} callback */ -Router.prototype._handleFindResult = function(params, contact, callback) { - var self = this; - var distance = utils.getDistance(this.hashedKey, contact.nodeID); - - this.contacted[contact.nodeID] = this.node._updateContact(contact); - - if (utils.compareKeys(distance, this.closestNodeDistance) === -1) { - this.previousClosestNode = this.closestNode; - this.closestNode = contact; - this.closestNodeDistance = distance; - } - - if(this.type === 'NODE') { - this._addToShortList(params.contacts); - return callback(); - } - - if(!params.value) { - this.contactsWithoutValue.push(contact); - this._addToShortList(params.contacts); - return callback(); - } - - var parsedValue; - try { - parsedValue = JSON.parse(params.value).value; - } catch(err) { - this.node._log.warn('failed to parse value %s', params.value); - return rejectContact(); - } - -this.node.validateKeyValuePair(this.key, parsedValue, function(isValid) { - if(!isValid) { - self.node._log.warn('failed to validate key/value pair for %s', self.key); +Router.prototype._handleFindResult = function (params, contact, callback) { + var self = this; + var distance = utils.getDistance(this.hashedKey, contact.nodeID); + + this.contacted[contact.nodeID] = this.node._updateContact(contact); + + if (utils.compareKeys(distance, this.closestNodeDistance) === -1) { + this.previousClosestNode = this.closestNode; + this.closestNode = contact; + this.closestNodeDistance = distance; + } + + if(this.type === 'NODE') { + this._addToShortList(params.contacts); + return callback(); + } + + if(!params.value) { + this.contactsWithoutValue.push(contact); + this._addToShortList(params.contacts); + return callback(); + } + + var parsedValue; + try { + parsedValue = JSON.parse(params.value).value; + } + catch (err) { + this.node._log.error('failed to parse value %s', params.value); return rejectContact(); } - self.foundValue = true; - self.value = parsedValue; + this.node.validateKeyValuePair(this.key, parsedValue, function(isValid) { + if(!isValid) { + self.node._log.warn('failed to validate key/value pair for %s', self.key); + return rejectContact(); + } - callback(); -}); + self.foundValue = true; + self.value = parsedValue; - function rejectContact() { - self._removeFromShortList(contact.nodeID); - callback(); - } + callback(); + }); + + + function rejectContact() { + self._removeFromShortList(contact.nodeID); + callback(); + } }; /** @@ -159,9 +179,11 @@ this.node.validateKeyValuePair(this.key, parsedValue, function(isValid) { * @param {array} contacts */ Router.prototype._addToShortList = function(contacts) { - assert(Array.isArray(contacts), 'No contacts supplied'); - this.shortlist = this.shortlist.concat(contacts); - this.shortlist = _.uniq(this.shortlist, false, 'nodeID'); + assert(Array.isArray(contacts), 'No contacts supplied'); + + this.shortlist = this.shortlist.concat(contacts); + this.shortlist = _.uniq(this.shortlist, false, 'nodeID'); + }; /** @@ -170,9 +192,15 @@ Router.prototype._addToShortList = function(contacts) { * @param {string} nodeID */ Router.prototype._removeFromShortList = function(nodeID) { - this.shortlist = _.reject(this.shortlist, function(c) { - return c.nodeID === nodeID; - }); + this.shortlist = _.reject(this.shortlist, function(c) { + return c.nodeID === nodeID; + }); + + for (var i = 0; i < this.shortlist.length; i++) { + if (!(this.shortlist[i] instanceof Contact)) { + throw new Error('Invalid contact'); + } + } }; /** @@ -181,28 +209,29 @@ Router.prototype._removeFromShortList = function(nodeID) { * @param {function} callback */ Router.prototype._handleQueryResults = function(callback) { - var self = this; + var self = this; - if (this.foundValue) { - return this._handleValueReturned(callback); - } + if (this.foundValue) { + return this._handleValueReturned(callback); + } - var closestNodeUnchanged = this.closestNode === this.previousClosestNode; - var shortlistFull = this.shortlist.length >= constants.K; + var closestNodeUnchanged = this.closestNode === this.previousClosestNode; + var shortlistFull = this.shortlist.length >= constants.K; - if (closestNodeUnchanged || shortlistFull) { - return callback(null, 'NODE', this.shortlist); - } + if (closestNodeUnchanged || shortlistFull) { + return callback(null, 'NODE', this.shortlist); + } - var remainingContacts = _.reject(this.shortlist, function(c) { - return self.contacted[c.nodeID]; - }); + var remainingContacts = _.reject(this.shortlist, function(c) { + return self.contacted[c.nodeID]; + }); - if (remainingContacts.length === 0) { - callback(null, 'NODE', this.shortlist); - } else { - this._iterativeFind(remainingContacts.splice(0, constants.ALPHA), callback); - } + if (remainingContacts.length === 0) { + callback(null, 'NODE', this.shortlist); + } + else { + this._iterativeFind(remainingContacts.splice(0, constants.ALPHA), callback); + } }; /** diff --git a/libs/transport/p2p/wotkad/lib/rpc.js b/libs/transport/p2p/wotkad/lib/rpc.js index a3eb831..4530bde 100644 --- a/libs/transport/p2p/wotkad/lib/rpc.js +++ b/libs/transport/p2p/wotkad/lib/rpc.js @@ -1,5 +1,20 @@ -/** -* @module kad/rpc +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -45,25 +60,29 @@ function RPC(options) { * @param {object} message * @param {function} callback */ -RPC.prototype.send = function(contact, message, callback) { - assert(contact instanceof Contact, 'Invalid contact supplied'); - assert(message instanceof Message, 'Invalid message supplied'); - - merge(message.params, { - rpcID: this._createMessageID() - }); - - var data = message.serialize(); - var offset = 0; - - this._send(data, contact); - - if (typeof callback === 'function') { - this._pendingCalls[message.params.rpcID] = { - timestamp: Date.now(), - callback: callback - }; - } +RPC.prototype.send = function (contact, message, callback) { + //assert(contact instanceof Contact, 'Invalid contact supplied'); + //if (!(contact instanceof Contact)) { + // return; + //} + assert(contact.address && contact.port, 'Invalid contact supplied'); + assert(message instanceof Message, 'Invalid message supplied'); + + merge(message.params, { + rpcID: this._createMessageID() + }); + + var data = message.serialize(); + var offset = 0; + + this._send(data, contact); + + if (typeof callback === 'function') { + this._pendingCalls[message.params.rpcID] = { + timestamp: Date.now(), + callback: callback + }; + } }; /** @@ -71,7 +90,7 @@ RPC.prototype.send = function(contact, message, callback) { * #close */ RPC.prototype.close = function() { - this._close(); + this._close(); }; /** @@ -118,15 +137,15 @@ RPC.prototype._handleMessage = function(buffer, info) { * #_expireCalls */ RPC.prototype._expireCalls = function() { - for (var rpcID in this._pendingCalls) { - var pendingCall = this._pendingCalls[rpcID]; - var timePassed = Date.now() - pendingCall.timestamp; - - if (timePassed > constants.T_RESPONSETIMEOUT) { - pendingCall.callback(new Error('RPC with ID `' + rpcID + '` timed out')); - delete this._pendingCalls[rpcID]; + for (var rpcID in this._pendingCalls) { + var pendingCall = this._pendingCalls[rpcID]; + var timePassed = Date.now() - pendingCall.timestamp; + + if (timePassed > constants.T_RESPONSETIMEOUT) { + pendingCall.callback(new Error('RPC with ID `' + rpcID + '` timed out')); + delete this._pendingCalls[rpcID]; + } } - } }; /* istanbul ignore next */ @@ -135,7 +154,7 @@ RPC.prototype._expireCalls = function() { * #_close */ RPC.prototype._close = function() { - throw new Error('Method not implemented'); + throw new Error('Method not implemented'); }; /* istanbul ignore next */ @@ -146,7 +165,7 @@ RPC.prototype._close = function() { * @param {Contact} contact */ RPC.prototype._send = function(data, contact) { - throw new Error('Method not implemented'); + throw new Error('Method not implemented'); }; /* istanbul ignore next */ @@ -156,7 +175,7 @@ RPC.prototype._send = function(data, contact) { * @param {object} options */ RPC.prototype._createContact = function(options) { - throw new Error('Method not implemented'); + throw new Error('Method not implemented'); }; module.exports = RPC; diff --git a/libs/transport/p2p/wotkad/lib/storages/localstorage.js b/libs/transport/p2p/wotkad/lib/storages/localstorage.js index 4099059..69da7bb 100644 --- a/libs/transport/p2p/wotkad/lib/storages/localstorage.js +++ b/libs/transport/p2p/wotkad/lib/storages/localstorage.js @@ -9,7 +9,7 @@ var EventEmitter = require('events').EventEmitter var map = new Map(); function KadLocalStorage(namespace) { - if (namespace.indexOf('_') >= 0) throw new Error('invalid namespace') + if (namespace.indexOf('_') >= 0) throw new Error('Invalid namespace, character "_" in the namespace and nick name is not allowed') this._prefix = namespace + '_' } diff --git a/libs/transport/p2p/wotkad/lib/transports/address-port-contact.js b/libs/transport/p2p/wotkad/lib/transports/address-port-contact.js index 718c9ab..0276f21 100644 --- a/libs/transport/p2p/wotkad/lib/transports/address-port-contact.js +++ b/libs/transport/p2p/wotkad/lib/transports/address-port-contact.js @@ -1,5 +1,20 @@ -/** -* @module kad/contact +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -18,26 +33,27 @@ inherits(AddressPortContact, Contact); */ function AddressPortContact(options) { - if (!(this instanceof AddressPortContact)) { - return new AddressPortContact(options); - } + if (!(this instanceof AddressPortContact)) { + return new AddressPortContact(options); + } - assert(options instanceof Object, 'Invalid options were supplied'); - assert(typeof options.address === 'string', 'Invalid address was supplied'); - assert(typeof options.port === 'number', 'Invalid port was supplied'); + assert(options instanceof Object, 'Invalid options were supplied'); + assert(typeof options.address === 'string', 'Invalid address was supplied'); + assert(typeof options.port === 'number', 'Invalid port was supplied'); - this.address = options.address; - this.port = options.port; + this.address = options.address; + this.port = options.port; - Contact.call(this, options) + Contact.call(this, options) } + /** * Generate a NodeID by taking the SHA1 hash of the address and port * #_createNodeID */ AddressPortContact.prototype._createNodeID = function() { - return utils.createID(this.toString()); + return utils.createID(this.toString()); }; /** @@ -45,7 +61,7 @@ AddressPortContact.prototype._createNodeID = function() { * #_toString */ AddressPortContact.prototype.toString = function() { - return this.address + ':' + this.port; + return this.address + ':' + this.port; }; module.exports = AddressPortContact; diff --git a/libs/transport/p2p/wotkad/lib/transports/index.js b/libs/transport/p2p/wotkad/lib/transports/index.js index 63bd801..c1f6a14 100644 --- a/libs/transport/p2p/wotkad/lib/transports/index.js +++ b/libs/transport/p2p/wotkad/lib/transports/index.js @@ -1,5 +1,20 @@ -/** -* @module kad/transports +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; diff --git a/libs/transport/p2p/wotkad/lib/transports/tcp.js b/libs/transport/p2p/wotkad/lib/transports/tcp.js index 183c42c..f9ff587 100644 --- a/libs/transport/p2p/wotkad/lib/transports/tcp.js +++ b/libs/transport/p2p/wotkad/lib/transports/tcp.js @@ -1,5 +1,20 @@ -/** -* @module kad/transports/tcp +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; diff --git a/libs/transport/p2p/wotkad/lib/transports/udp.js b/libs/transport/p2p/wotkad/lib/transports/udp.js index 96bb88b..fe734bf 100644 --- a/libs/transport/p2p/wotkad/lib/transports/udp.js +++ b/libs/transport/p2p/wotkad/lib/transports/udp.js @@ -1,5 +1,20 @@ -/** -* @module kad/transports/udp +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; @@ -17,34 +32,34 @@ inherits(UDPTransport, RPC); * @param {object} options */ function UDPTransport(options) { - if (!(this instanceof UDPTransport)) { - return new UDPTransport(options); - } + if (!(this instanceof UDPTransport)) { + return new UDPTransport(options); + } - var self = this; - var socketOptions = { type: 'udp4', reuseAddr: true }; - var socketMessageHandler = this._handleMessage.bind(this); + var self = this; + var socketOptions = { type: 'udp4', reuseAddr: true }; + var socketMessageHandler = this._handleMessage.bind(this); - RPC.call(this, options); + RPC.call(this, options); - this._socket = dgram.createSocket(socketOptions, socketMessageHandler); + this._socket = dgram.createSocket(socketOptions, socketMessageHandler); - this._socket.on('error', function(err) { - var contact = self._contact; - self._log.warn('failed to bind to supplied address %s', contact.address); - self._log.info('binding to all interfaces as a fallback'); - self._socket.close(); + this._socket.on('error', function(err) { + var contact = self._contact; + self._log.warn('failed to bind to supplied address %s', contact.address); + self._log.info('binding to all interfaces as a fallback'); + self._socket.close(); - self._socket = dgram.createSocket(socketOptions, socketMessageHandler); + self._socket = dgram.createSocket(socketOptions, socketMessageHandler); - self._socket.bind(contact.port); - }); + self._socket.bind(contact.port); + }); - this._socket.on('listening', function() { - self.emit('ready'); - }); + this._socket.on('listening', function() { + self.emit('ready'); + }); - this._socket.bind(this._contact.port, this._contact.address); + this._socket.bind(this._contact.port, this._contact.address); } /** @@ -53,7 +68,7 @@ function UDPTransport(options) { * @param {object} options */ UDPTransport.prototype._createContact = function(options) { - return new AddressPortContact(options); + return new AddressPortContact(options); } /** @@ -63,7 +78,7 @@ UDPTransport.prototype._createContact = function(options) { * @param {Contact} contact */ UDPTransport.prototype._send = function(data, contact) { - this._socket.send(data, 0, data.length, contact.port, contact.address); + this._socket.send(data, 0, data.length, contact.port, contact.address); }; /** @@ -71,7 +86,7 @@ UDPTransport.prototype._send = function(data, contact) { * #_close */ UDPTransport.prototype._close = function() { - this._socket.close(); + this._socket.close(); }; module.exports = UDPTransport; diff --git a/libs/transport/p2p/wotkad/lib/utils.js b/libs/transport/p2p/wotkad/lib/utils.js index 3a4dd01..ff445c7 100644 --- a/libs/transport/p2p/wotkad/lib/utils.js +++ b/libs/transport/p2p/wotkad/lib/utils.js @@ -1,5 +1,20 @@ -/** -* @module kad/utils +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + */ 'use strict'; diff --git a/libs/transport/p2p/wotkad/peer_network.js b/libs/transport/p2p/wotkad/peer_network.js index 4ba5e5f..7d67736 100644 --- a/libs/transport/p2p/wotkad/peer_network.js +++ b/libs/transport/p2p/wotkad/peer_network.js @@ -1,4 +1,23 @@ -var util = require("util"); +/* + +This file is part of W3C Web-of-Things-Framework. + +W3C Web-of-Things-Framework is an open source project to create an Internet of Things framework. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +W3C Web-of-Things-Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with W3C Web-of-Things-Framework. If not, see . + +File created by Tibor Zsolt Pardi + +Copyright (C) 2015 The W3C WoT Team + +*/ + +var util = require("util"); var events = require("events"); var assert = require('assert'); var async = require('async'); @@ -14,6 +33,7 @@ function PeerNetwork() { events.EventEmitter.call(this); + this.stored_items = {}; } PeerNetwork.prototype.create_peer = function (options) { @@ -22,24 +42,46 @@ PeerNetwork.prototype.create_peer = function (options) { assert(options.address, 'No address supplied'); assert(options.port, 'No port supplied'); assert(options.nick, 'No nick supplied'); + assert(options.alg, 'No acryptography algorithm supplied'); + assert(options.private_key, 'No private key supplied'); + assert(options.public_key, 'No private key supplied'); var peernode = wotkad({ address: options.address, port: options.port, nick: options.nick, + alg: options.alg, + private_key: options.private_key, + public_key: options.public_key, seeds: options.seeds, validateKeyValuePair: this.validate_keyvaluepair }); + peernode.on('store', this.msg_stored.bind(this)); + return peernode; } -PeerNetwork.prototype.validate_keyvaluepair = function (key, value, callback) { - callback(true); +PeerNetwork.prototype.msg_stored = function (node_id, item) { + if (!item || !item.key || !item.hash) + return; + + var hash = item.hash; + var key = item.key; + var stored = this.stored_items[key]; + if (stored == hash) { + return; + } + + this.stored_items[key] = hash; + this.emit("data", key); } +PeerNetwork.prototype.validate_keyvaluepair = function (key, value, callback) { + callback(true); +} module.exports = PeerNetwork; \ No newline at end of file diff --git a/package.json b/package.json index 692171d..6e6b4bf 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "async": "^1.5.0", + "bigi": "^1.4.1", "body-parser": "^1.13.2", "clarinet": "^0.11.0", "coap": "^0.12.0", diff --git a/transports/p2p/handler.js b/transports/p2p/handler.js index 10235f9..d94eb50 100644 --- a/transports/p2p/handler.js +++ b/transports/p2p/handler.js @@ -1,23 +1,35 @@ var assert = require('assert'); var logger = require('../../logger'); -var wotkad = require('../../libs/transport/p2p/wotkad'); +var PeerNetwork = require('../../libs/transport/p2p/wotkad/peer_network'); exports.start = function start(settings) { try { logger.info('Bootstrap P2P network, initiate seed node'); - // start the P2P overlay networks and Kademlia DHT + if (!settings || !settings.nodes || !settings.nodes.length) { + throw new Error("Invalid P2P configuration settings"); + } - assert(settings.address, 'No p2p address is specified'); - assert(settings.port, 'No p2p port is specified'); + // start the P2P overlay networks and Kademlia DHT + var peernet = new PeerNetwork(); - // Create our first node - var seed_node = wotkad({ - transport: wotkad.transports.UDP, - address: '127.0.0.1', - port: 65530, - nick: 'wotseed01' - }); + for (var i = 0; i < settings.nodes.length; i++) { + var node = settings.nodes[i]; + assert(node.address, 'No p2p address is specified'); + assert(node.port, 'No p2p port is specified'); + assert(node.nick, 'No p2p nick is specified'); + + // Create our first node + var seed_node = peernet.create_peer({ + address: node.address, + port: node.port, + nick: node.nick, + alg: {}, + private_key: {}, + public_key: {}, + seeds: node.seeds + }); + } logger.info('P2P overlay network started'); }